[Jpeergen-cvs] dev/src/java/org/progeeks/jni BaseCodelet.java,NONE,1.1 Codelet.java,NONE,1.1 JavahCo
Status: Beta
Brought to you by:
pspeed
|
From: <ps...@us...> - 2004-08-12 21:11:21
|
Update of /cvsroot/jpeergen/dev/src/java/org/progeeks/jni In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5688/src/java/org/progeeks/jni Added Files: BaseCodelet.java Codelet.java JavahCodelet.java JniCodelet.java JniStubCodelet.java NameUtils.java PeerBindingCodelet.java PeerClassCodelet.java PeerDoclet.java PeerInterfaceCodelet.java Log Message: Initial source check-in. --- NEW FILE: BaseCodelet.java --- /* * $Id: BaseCodelet.java,v 1.1 2004/08/12 21:11:03 pspeed Exp $ * * Copyright (c) 2004, Paul Speed * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) Neither the names "Progeeks", "JPeerGen", nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.progeeks.jni; import java.io.*; import java.util.*; import com.sun.javadoc.*; /** * Base abstract implementation of the codelet interface. * Provides default implementations of most methods as well * as enhanced access to the internal print writer. * * @version $Revision: 1.1 $ * @author Paul Speed */ public abstract class BaseCodelet implements Codelet { protected Properties settings; protected PrintWriter out; protected RootDoc root; protected ClassDoc currentClass; protected String indent = ""; /** * Initialized the codelet to work with the specified * root doc structure. */ public void initialize( RootDoc root, Properties settings ) { this.root = root; this.settings = settings; } /** * Returns true if the codelet should always overwrite * existing files. The default implementation returns * true. */ public boolean overwrite() { return( true ); } /** * Prints a complete line including the current indent. */ protected void printLine( String line ) { printBegin( line ); out.println(); } /** * Prints the indent and the specified text, but * does not do a line feed. */ protected void printBegin( String s ) { out.print( indent ); out.print( s ); } /** * Prints the specified string to the current line with * no indent. */ protected void printMid( String s ) { out.print( s ); } /** * Prints the specified string to the current line with * no indent and performs a linefeed. */ protected void printEnd( String s ) { out.println( s ); } /** * Sends a linefeed to the generated file. */ protected void newLine() { out.println(); } /** * Pushes another indent onto the indent stack. */ protected void pushIndent() { // I fully recognize that there's a better way to do // an indent stack (ie: char array), but that's considerably // more coding. indent += " "; } /** * Pops an indent off of the indent stack. */ protected void popIndent() { int len = indent.length(); if( len < 4 ) return; indent = indent.substring( 4 ); } /** * Starts the generation of the specified output using * the specified writer and class information. */ public void beginFile( Writer out, ClassDoc classDoc ) { if( this.currentClass != null ) throw new IllegalStateException( "Previous class was not completed." ); if( out instanceof PrintWriter ) this.out = (PrintWriter)out; else this.out = new PrintWriter(out); this.currentClass = classDoc; // Write out the initial generated code writeHeader(); } /** * Ends any previously specified file. */ public void endFile() { if( this.currentClass == null ) throw new IllegalStateException( "No class was started." ); // Write out the footer and clear the state writeFooter(); out = null; currentClass = null; indent = ""; } /** * Writes out the beginning of the file. */ protected abstract void writeHeader(); /** * Writes out the end of the file. */ protected abstract void writeFooter(); /** * Returns the file name that should be used to generate * the specified class based on the specific codelet * implementation. */ public abstract String getFileName( ClassDoc classDoc ); /** * Writes out the generated code for the specified field * definition based on the specific codelet implementation. * The default implementation does nothing. */ public void writeField( FieldDoc field ) { } /** * Writes out the generated code for the specified method * definition based on the specific codelet implementation. */ public abstract void writeMethod( MethodDoc method ); } --- NEW FILE: Codelet.java --- /* * $Id: Codelet.java,v 1.1 2004/08/12 21:11:03 pspeed Exp $ * * Copyright (c) 2004, Paul Speed * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) Neither the names "Progeeks", "JPeerGen", nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.progeeks.jni; import java.io.*; import java.util.*; import com.sun.javadoc.*; /** * Defines the interface to which all codelets will comply. Codelets are * used by the PeerDoclet to generate specific output files. * * @version $Revision: 1.1 $ * @author Paul Speed */ public interface Codelet { /** * Initialized the codelet to work with the specified * root doc structure. */ public void initialize( RootDoc root, Properties settings ); /** * Returns true if the codelet should always overwrite * existing files. */ public boolean overwrite(); /** * Starts the generation of the specified output using * the specified writer and class information. */ public void beginFile( Writer out, ClassDoc classDoc ); /** * Ends any previously specified file. */ public void endFile(); /** * Returns the file name that should be used to generate * the specified class based on the specific codelet * implementation. */ public String getFileName( ClassDoc classDoc ); /** * Writes out the generated code for the specified field * definition based on the specific codelet implementation. */ public void writeField( FieldDoc field ); /** * Writes out the generated code for the specified method * definition based on the specific codelet implementation. */ public void writeMethod( MethodDoc method ); } --- NEW FILE: JavahCodelet.java --- /* * $Id: JavahCodelet.java,v 1.1 2004/08/12 21:11:03 pspeed Exp $ * * Copyright (c) 2004, Paul Speed * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) Neither the names "Progeeks", "JPeerGen", nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.progeeks.jni; import java.util.*; import com.sun.javadoc.*; /** * A codelet that mimics the JNI javah function. This * allows us to test some of our other naming stuff and also * gives us more control over .h file generation. * * @version $Revision: 1.1 $ * @author Paul Speed */ public class JavahCodelet extends JniCodelet { /** * Writes out the beginning of the file. */ protected void writeHeader() { printLine( "/* DO NOT EDIT THIS FILE - it is machine generated */" ); include( "jni.h", false ); String fullName = NameUtils.mangle(currentClass.qualifiedName()); printLine( "/* Header for class " + fullName + " */" ); newLine(); printLine( "#ifndef _Included_" + fullName ); printLine( "#define _Included_" + fullName ); printLine( "#ifdef __cplusplus" ); printLine( "extern \"C\" {" ); printLine( "#endif" ); } /** * Writes out the end of the file. */ protected void writeFooter() { printLine( "#ifdef __cplusplus" ); printLine( "}" ); printLine( "#endif" ); printLine( "#endif" ); } /** * Returns the file name that should be used to generate * the specified class based on the specific codelet * implementation. */ public String getFileName( ClassDoc classDoc ) { return( NameUtils.javahFileName( classDoc ) ); } /** * Writes out the generated code for the specified method * definition based on the specific codelet implementation. */ public void writeMethod( MethodDoc m ) { if( m.isNative() ) { printComment( m ); printBegin( "JNIEXPORT " ); printMid( getJniType(m.returnType()) ); printMid( " JNICALL " ); printEnd( NameUtils.jniMethodName(m.name(), m.containingClass().qualifiedName()) ); if( m.isStatic() ) { printBegin( " (JNIEnv *, jclass" ); } else { printBegin( " (JNIEnv *, jobject" ); } // Print out the rest of the parameters Parameter[] parms = m.parameters(); for( int i = 0; i < parms.length; i++ ) { printMid( ", " ); printMid( getJniType(parms[i].type()) ); } printEnd( ");" ); newLine(); } } private void printComment( MethodDoc m ) { String commentText = m.commentText(); StringTokenizer st = new StringTokenizer( commentText, "\r\n" ); printLine( "/" + "*" ); boolean hasComments = false; /* while( st.hasMoreTokens() ) { String line = st.nextToken(); printLine( " * " + line.trim() ); hasComments = true; }*/ ClassDoc c = m.containingClass(); PackageDoc p = c.containingPackage(); if( hasComments ) printLine( " *" ); printLine( " * Class: " + NameUtils.mangle( c.qualifiedName() ) ); printLine( " * Method: " + NameUtils.mangle( m.name() ) ); printLine( " * Signature: " + NameUtils.getSignature( m ) ); printLine( " */" ); } } --- NEW FILE: JniCodelet.java --- /* * $Id: JniCodelet.java,v 1.1 2004/08/12 21:11:03 pspeed Exp $ * * Copyright (c) 2004, Paul Speed * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) Neither the names "Progeeks", "JPeerGen", nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.progeeks.jni; import java.util.*; import com.sun.javadoc.*; /** * Abstract codelet containing lots of useful functions * for doing the JNI code generation. * * @version $Revision: 1.1 $ * @author Paul Speed */ public abstract class JniCodelet extends BaseCodelet { private ClassDoc classDoc; private ClassDoc throwableDoc; private static Map jniToC = new HashMap(); private static Map javaToC = new HashMap(); private static Map javaToJni = new HashMap(); private static Map jniMethods = new HashMap(); private static Map converters = new HashMap(); static { // There are multiple types of type conversions needed. // Given the Java type we need to figure out what the type and // conversion needs to be at several steps along the way. And in some // cases, we use a different conversion for method parameters when // it is more convenient. // // Two directions: Java calling C and C calling Java. // When C is calling Java we don't want to make the // caller create a std::string just so that the JNI code // can strip out its c_str(). So we define those to take // char * instead. Going the other way, std::string is // probably better. // // C calling Java: // int -> jint // char * -> jstring // returns: // jint -> int // jstring -> std::string // // Java calling C: // jint -> int // jstring -> std::string // returns: // int -> jint // std::string -> jstring // // Future note: // The real issue is that type conversion is difficult when // the "value" is long and printed out using several print()s. // Some sort of stack approach might be useful for prefixing // and suffixing sections of code. Code could even be nested // in such a way that prefixes could be added while the body // is being generated... since everything would be committed // after the fact. // Pres and posts could be added to the current section, the // method section. // Although, it would probably be just as easy to have // method objects that get instantiated with the necessary // values and can generate their own pres and posts. // Still might be useful to have a pushWrapper or somesuch. // pushWrapper( getConverter( "foo" ) ); // Prints initial part of conversion // writeMethodCall(); // print the method call stuff. // popWrapper(); // prints the last part of conversion. // // wrapper can also have a direct method for printing a variable // value: // wrapper.writeVar( "foo" ); // which would automatically wrap the value and print it in the current // scope. // So, type conversion should really be a set of wrapper classes for // the different cases. // // // String conversions: // char * -> jstring (function args, C calling Java) // std::string -> jstring (function return, java calling C) // jstring -> std::string (function return, C calling java) // jstring -> std::string (function arg, Java calling C) // // Java Type | C Arg | C Return | JNI Return | JNI Arg | C arg when called from Java //------------------------------------------------------------------------- // java.lang.String | char * | std::string | jstring | jstring | std::string // // Java calling C method declaration: // std::string fooMethod( std::string bar ); // (needs to convert jstring to std::string and then std::string to jstring) // // C calling Java method declaration: // std::string fooMethod( char *bar ); // (needs to convert char * to jstring and then jstring to std::string) // // Everything is very straight forward except for the C calling Java argument // case. So, perhaps just special case that to make the code simpler in the // general case. A conversion object can still be provided since we know we need it. // // Register the various type conversions // Note: these are actual code types on the C side and so only // include JNI types or C types. Java isn't relevant in this section. registerConverter( "bool", "jboolean", new TypeConverter( "(jboolean)" ) ); registerConverter( "char", "jbyte", new TypeConverter( "(jbyte)" ) ); registerConverter( "char", "jchar", new TypeConverter( "(jchar)" ) ); registerConverter( "short", "jshort", new TypeConverter( "(jshort)" ) ); registerConverter( "int", "jint", new TypeConverter( "(jint)" ) ); registerConverter( "long", "jlong", new TypeConverter( "(jlong)" ) ); registerConverter( "float", "jfloat", new TypeConverter( "(jfloat)" ) ); registerConverter( "double", "jdouble", new TypeConverter( "(jdouble)" ) ); registerConverter( "std::string", "jstring", new TypeConverter( "env->NewStringUTF(", ".c_str())" ) ); registerConverter( "const char *", "jstring", new TypeConverter( "env->NewStringUTF(", ")" ) ); // And the other way registerConverter( "jboolean", "bool", new TypeConverter( "(", " == 1)" ) ); registerConverter( "jbyte", "char", new TypeConverter( "(char)" ) ); registerConverter( "jchar", "char", new TypeConverter( "(char)" ) ); registerConverter( "jshort", "short", new TypeConverter( "(short)" ) ); registerConverter( "jint", "int", new TypeConverter( "(int)" ) ); registerConverter( "jlong", "long", new TypeConverter( "(long)" ) ); registerConverter( "jfloat", "float", new TypeConverter( "(float)" ) ); registerConverter( "jdouble", "double", new TypeConverter( "(double)" ) ); registerConverter( "jstring", "std::string", new TypeConverter( "JniUtils::getString(env, (jstring)", ")" ) ); // Note: the above use a dual-map internally which is probably not necessary // now that all the conversions are mapped out. Still, it provides a certain // clarity and flexibility so I'm not going to remove it yet. javaToC.put( "boolean", "bool" ); javaToC.put( "byte", "char" ); javaToC.put( "char", "char" ); javaToC.put( "short", "short" ); javaToC.put( "int", "int" ); javaToC.put( "long", "long" ); javaToC.put( "float", "float" ); javaToC.put( "double", "double" ); javaToC.put( "void", "void" ); javaToC.put( "java.lang.String", "std::string" ); jniToC.put( "jboolean", "bool" ); jniToC.put( "jbyte", "char" ); jniToC.put( "jchar", "char" ); jniToC.put( "jshort", "short" ); jniToC.put( "jint", "int" ); jniToC.put( "jlong", "long" ); jniToC.put( "jfloat", "float" ); jniToC.put( "jdouble", "double" ); jniToC.put( "jstring", "std::string" ); jniToC.put( "void", "void" ); javaToJni.put( "boolean", "jboolean" ); javaToJni.put( "byte", "jbyte" ); javaToJni.put( "char", "jchar" ); javaToJni.put( "short", "jshort" ); javaToJni.put( "int", "jint" ); javaToJni.put( "long", "jlong" ); javaToJni.put( "float", "jfloat" ); javaToJni.put( "double", "jdouble" ); javaToJni.put( "void", "void" ); jniMethods.put( "jobject", "ObjectMethod" ); jniMethods.put( "boolean", "BooleanMethod" ); jniMethods.put( "byte", "ByteMethod" ); jniMethods.put( "char", "CharMethod" ); jniMethods.put( "short", "ShortMethod" ); jniMethods.put( "int", "IntMethod" ); jniMethods.put( "long", "LongMethod" ); jniMethods.put( "float", "FloatMethod" ); jniMethods.put( "double", "DoubleMethod" ); } /** * Initialized the codelet to work with the specified * root doc structure. */ public void initialize( RootDoc root, Properties settings ) { super.initialize( root, settings ); // Setup some predefined classes classDoc = root.classNamed( "java.lang.Class" ); throwableDoc = root.classNamed( "java.lang.Throwable" ); } /** * Starts a comment section. Anything writtern after * will be in a comment block. */ protected void beginComment() { printLine( "/" + "*" ); indent += " * "; } /** * Ends a comment section. */ protected void endComment() { // Take the " * " off the indent stack int len = indent.length(); if( len < 4 ) return; indent = indent.substring( 0, len - 4 ); printLine( " */" ); } /** * Begins a {} section. */ protected void beginSection() { printLine( "{" ); pushIndent(); } /** * Ends a {} section. */ protected void endSection() { popIndent(); printLine( "}" ); } /** * Adds a #include statement. */ protected void include( String file, boolean local ) { if( local ) printLine( "#include \"" + file + "\"" ); else printLine( "#include <" + file + ">" ); } /** * Returns true if the specified type is freely castable * between C++ and JNI. For example, int and jint would * both return true. */ protected boolean isCastableType( String name ) { // Void doesn't count if( "void".equals( name ) ) return( true ); // If there's a converter set for this type then // it is obviously convertable. if( converters.containsKey( name ) ) return( true ); // Otherwise, we may have been passed a Java type // so check to see if the Jni type is convertable String jniType = (String)javaToJni.get(name); if( jniType != null && converters.containsKey( jniType ) ) return( true ); return( false ); } /** * Returns the castable C type for the specified JNI * type. */ protected String getCType( String name ) { String value = (String)javaToC.get( name ); if( value != null ) return( value ); if( "void".equals( name ) ) return( "void" ); return( null ); } protected String getJniType( String name ) { String value = (String)javaToJni.get( name ); if( value != null ) return( value ); if( "void".equals( name ) ) return( "void" ); return( "jobject" ); } /** * Returns true if the Java type can be directly * cast to a C type. */ protected boolean isCastableType( Type type ) { // If the type is the same as ourselves, then we can in fact // auto-convert it in most cases //if( type.qualifiedTypeName().equals( currentClass.qualifiedName() ) ) // { // return( true ); // } // While the above statement may be true, it's going to take a lot // of work... nor does it allow static-only class peering which is // a useful utility. // Short-cut // If getJniType returns something that is // still in the casting maps then we are ok. String t = getJniType(type); return( isCastableType(t) ); } protected String convertValueToC( Type type, String var ) { if( type.qualifiedTypeName().equals( currentClass.qualifiedName() ) ) { return( "getPeer( " + var + " )" ); } TypeConverter conv = getConverter( getJniType(type), getCType(type) ); if( conv == null ) { System.out.println( "No converter found for: " + getJniType(type) + "->" + getCType(type) ); } return( conv.getPrefix() + var + conv.getSuffix() ); } protected String convertValueToJni( Type type, String var ) { TypeConverter conv = getConverter( getCType(type), getJniType(type) ); return( conv.getPrefix() + var + conv.getSuffix() ); } protected String convertValueToJniStart( Type type ) { TypeConverter conv = getConverter( getCType(type), getJniType(type) ); return( conv.getPrefix() ); } protected String convertValueToJniEnd( Type type ) { TypeConverter conv = getConverter( getCType(type), getJniType(type) ); return( conv.getSuffix() ); } protected String getCType( Type type ) { String name = type.qualifiedTypeName(); if( name.equals( currentClass.qualifiedName() ) ) { // Return our peer class return( NameUtils.peerClassName( currentClass ) + "*" ); } String cast = (String)javaToC.get(name); if( cast != null ) return(cast); return( null ); } /** * Special version of getCType that special cases the string * type to return char *. */ protected String getSimpleCType( Type type ) { String name = type.qualifiedTypeName(); if( name.equals( currentClass.qualifiedName() ) ) { // Return our peer class return( NameUtils.peerClassName( currentClass ) + "*" ); } // Special case the strings if( name.equals( "java.lang.String" ) ) return( "const char *" ); String cast = (String)javaToC.get(name); if( cast != null ) return(cast); return( null ); } protected String getJniMethodCall( MethodDoc m ) { Type type = m.returnType(); String name = type.qualifiedTypeName(); ClassDoc cDoc = type.asClassDoc(); String dim = type.dimension(); if( dim != null && dim.length() > 0 ) { return( (String)"Call" + (m.isStatic()?"Static":"") + jniMethods.get( "jobject" ) ); } else if( cDoc == null ) { return( (String)"Call" + (m.isStatic()?"Static":"") + jniMethods.get( name ) ); } return( (String)"Call" + (m.isStatic()?"Static":"") + jniMethods.get( "jobject" ) ); } protected String getJniType( Type type ) { String name = type.qualifiedTypeName(); ClassDoc cDoc = type.asClassDoc(); String dim = type.dimension(); if( dim != null && dim.length() > 0 ) { if( dim.equals( "[]" ) ) return( getJniType( name ) + "Array" ); else return( "jobjectArray" ); } else if( cDoc == null ) { return( getJniType( name ) ); } else if( "java.lang.String".equals( name ) ) { return( "jstring" ); } else if( cDoc.subclassOf( classDoc ) ) { return( "jclass" ); } else if( cDoc.subclassOf( throwableDoc ) ) { return( "jthrowable" ); } return( "jobject" ); } public static TypeConverter getConverter( String from, String to ) { Map map = (Map)converters.get( from ); if( map == null ) return( null ); return( (TypeConverter)map.get( to ) ); } public static void registerConverter( String from, String to, TypeConverter converter ) { Map map = (Map)converters.get( from ); if( map == null ) { map = new HashMap(); converters.put( from, map ); } map.put( to, converter ); } /** * Outputs the necessary prefix and suffix code to do a * type conversion from one type to another. */ public static class TypeConverter { private String prefix; private String suffix; public TypeConverter( String prefix, String suffix ) { this.prefix = prefix; this.suffix = suffix; } public TypeConverter( String prefix ) { this( prefix, "" ); } public String getConvertedValue( String value ) { return( prefix + value + suffix ); } public String getPrefix() { return( prefix ); } public String getSuffix() { return( suffix ); } } } --- NEW FILE: JniStubCodelet.java --- /* * $Id: JniStubCodelet.java,v 1.1 2004/08/12 21:11:03 pspeed Exp $ * * Copyright (c) 2004, Paul Speed * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) Neither the names "Progeeks", "JPeerGen", nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.progeeks.jni; import java.util.*; import com.sun.javadoc.*; /** * A codelet that generates the .cpp stub file * for a JNI peer. * * @version $Revision: 1.1 $ * @author Paul Speed */ public class JniStubCodelet extends JniCodelet { /** * Writes out the beginning of the file. */ protected void writeHeader() { printLine( "/* DO NOT EDIT THIS FILE - it is machine generated */" ); include( "jni.h", false ); include( NameUtils.javahFileName( currentClass ), true ); include( NameUtils.peerInterfaceFileName( currentClass ), true ); newLine(); beginComment(); String fullName = NameUtils.mangle(currentClass.qualifiedName()); printLine( "Stub peer functions for class " + fullName + "." ); printLine( "This looks up the appropriate peer object and calls the " ); printLine( "associated peer methods." ); endComment(); newLine(); printLine( "#ifdef __cplusplus" ); printLine( "extern \"C\" {" ); printLine( "#endif" ); newLine(); // Also need to add some additional includes and/or add // some generated methods for creating the C++ peer, etc. } /** * Returns a mangled method name for methods private to * the generated file. Since this is essentially a C file, * we need to make sure that these methods are unique... * so we generate method names similar to the actual stub * methods. */ protected String getMethodName( String methodName ) { StringBuffer result = new StringBuffer(); result.append( NameUtils.mangle( currentClass.qualifiedName() ) ); result.append( "_" ); result.append( NameUtils.mangle( methodName ) ); return( result.toString() ); } /** * Writes out the end of the file. */ protected void writeFooter() { printLine( "#ifdef __cplusplus" ); printLine( "}" ); printLine( "#endif" ); } /** * Returns the file name that should be used to generate * the specified class based on the specific codelet * implementation. */ public String getFileName( ClassDoc classDoc ) { return( NameUtils.jniFileName( classDoc ) ); } /** * Writes out the generated code for the specified method * definition based on the specific codelet implementation. */ public void writeMethod( MethodDoc m ) { String name = NameUtils.peerClassName(currentClass); if( m.isNative() ) { printComment( m ); printBegin( "JNIEXPORT " ); printMid( getJniType(m.returnType()) ); printMid( " JNICALL " ); printEnd( NameUtils.jniMethodName(m.name(), m.containingClass().qualifiedName()) ); if( m.isStatic() ) { printBegin( " (JNIEnv *env, jclass selfClass" ); } else { printBegin( " (JNIEnv *env, jobject self" ); } // Print out the rest of the parameters Parameter[] parms = m.parameters(); for( int i = 0; i < parms.length; i++ ) { printMid( ", " ); printMid( getJniType(parms[i].type()) ); printMid( " " ); printMid( parms[i].name() ); } printEnd( ")" ); beginSection(); if( !m.isStatic() ) { printLine( "// Lookup peer" ); printBegin( name ); printMid( " *peer = " ); printMid( name + "::getPeer" ); if( m.isStatic() ) // we don't really handle the static case printEnd( "( env, selfClass );" ); else printEnd( "( env, self );" ); newLine(); printLine( "// Call peer method" ); if( !"void".equals( m.returnType().typeName() ) ) printBegin( "return " ); else printBegin( "" ); printMid( "peer->" ); printMid( NameUtils.peerMethodName( m.name(), m.containingClass().qualifiedName()) ); printMid( "( env, self" ); } else { if( !"void".equals( m.returnType().typeName() ) ) printBegin( "return " ); else printBegin( "" ); // A much simpler call can be made printMid( name ); printMid( "::" ); printMid( NameUtils.peerMethodName( m.name(), m.containingClass().qualifiedName()) ); printMid( "( env, selfClass" ); } for( int i = 0; i < parms.length; i++ ) { printMid( ", " ); printMid( parms[i].name() ); } printEnd( ");" ); endSection(); newLine(); } } private void printComment( MethodDoc m ) { String commentText = m.commentText(); StringTokenizer st = new StringTokenizer( commentText, "\r\n" ); printLine( "/" + "*" ); boolean hasComments = false; while( st.hasMoreTokens() ) { String line = st.nextToken(); printLine( " * " + line.trim() ); hasComments = true; } ClassDoc c = m.containingClass(); PackageDoc p = c.containingPackage(); if( hasComments ) printLine( " *" ); printLine( " * Class: " + NameUtils.mangle( c.qualifiedName() ) ); printLine( " * Method: " + NameUtils.mangle( m.name() ) ); printLine( " * Signature: " + NameUtils.getSignature( m ) ); printLine( " */" ); } } --- NEW FILE: NameUtils.java --- /* * $Id: NameUtils.java,v 1.1 2004/08/12 21:11:03 pspeed Exp $ * * Copyright (c) 2004, Paul Speed * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) Neither the names "Progeeks", "JPeerGen", nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.progeeks.jni; import com.sun.javadoc.*; /** * Utility methods for naming methods and classes, etc. * * @version $Revision: 1.1 $ * @author Paul Speed */ public class NameUtils { /** * Returns the signature string for the specified method * definition. */ public static String getSignature( MethodDoc m ) { StringBuffer sb = new StringBuffer( "(" ); Parameter[] parms = m.parameters(); for( int i = 0; i < parms.length; i++ ) { sb.append( getSignatureType(parms[i].type()) ); } sb.append( ")" ); sb.append( getSignatureType(m.returnType()) ); return( sb.toString() ); } /** * Returns the method name as will be used in the jni stub. */ public static String jniMethodName( String methodName, String className ) { StringBuffer result = new StringBuffer(); result.append( "Java_" ); result.append( mangle( className ) ); result.append( "_" ); result.append( mangle( methodName ) ); return( result.toString() ); } /** * Returns the method name as will be used in the C++ peer class. */ public static String peerMethodName( String methodName, String className ) { StringBuffer result = new StringBuffer(); result.append( "Java_" ); result.append( mangle( methodName ) ); return( result.toString() ); } /** * Returns the file name for the javah generated header file. */ public static String javahFileName( ClassDoc classDoc ) { StringBuffer result = new StringBuffer(); result.append( mangle(classDoc.qualifiedName()) ); result.append( ".h" ); return( result.toString() ); } /** * Returns the file name for the C++ jni stub to match the * javah generated header file. */ public static String jniFileName( ClassDoc classDoc ) { StringBuffer result = new StringBuffer(); result.append( mangle(classDoc.qualifiedName()) ); result.append( "JniBinding.cpp" ); return( result.toString() ); } /** * Returns the C++ class name for the peer. */ public static String peerClassName( ClassDoc classDoc ) { return( mangle(classDoc.name()) + "Peer" ); } /** * Returns the file name for the C++ peer class. */ public static String peerClassFileName( ClassDoc classDoc ) { StringBuffer result = new StringBuffer(); result.append( peerClassName(classDoc) ); result.append( ".cpp" ); return( result.toString() ); } /** * Returns the file name for the C++ peer interface. */ public static String peerInterfaceFileName( ClassDoc classDoc ) { StringBuffer result = new StringBuffer(); result.append( peerClassName(classDoc) ); result.append( ".h" ); return( result.toString() ); } /** * Returns the file name for the C++ peer binding implementations * that will be included in the peer interface header. */ public static String bindingFileName( ClassDoc classDoc ) { StringBuffer result = new StringBuffer(); result.append( mangle(classDoc.name()) ); result.append( "Bindings.h" ); return( result.toString() ); } /** * Mangles the string value according the jni method naming * specs. */ public static String mangle( String s ) { StringBuffer result = new StringBuffer(); int len = s.length(); for( int i = 0; i < len; i++ ) { char c = s.charAt(i); if( Character.isLetterOrDigit( c ) ) { result.append( c ); continue; } switch( c ) { case '_': result.append( "_1" ); break; case '.': result.append( "_" ); break; case ';': result.append( "_2" ); break; case '[': result.append( "_3" ); break; default: result.append( mangleChar(c) ); break; } } return( result.toString() ); } /** * Properly encodes a character to meet the jni method naming * specs. */ public static String mangleChar( char c ) { String hex = Integer.toHexString(c); char[] result = new char[] { '_', '0', '0', '0', '0', '0' }; int dest = (5 - hex.length()) + 1; for( int src = 0; src < hex.length(); src++ ) result[dest++] = hex.charAt(src); return( new String(result) ); } public static String getSignatureType( String name ) { if( "boolean".equals( name ) ) return( "Z" ); else if( "byte".equals( name ) ) return( "B" ); else if( "char".equals( name ) ) return( "C" ); else if( "short".equals( name ) ) return( "S" ); else if( "int".equals( name ) ) return( "I" ); else if( "long".equals( name ) ) return( "J" ); else if( "float".equals( name ) ) return( "F" ); else if( "double".equals( name ) ) return( "D" ); else if( "void".equals( name ) ) return( "V" ); return( "L" + name.replace( '.', '/' ) + ";" ); } public static String getSignatureType( Type type ) { String name = type.qualifiedTypeName(); ClassDoc cDoc = type.asClassDoc(); String dim = type.dimension(); if( dim != null && dim.length() > 0 ) { StringBuffer sb = new StringBuffer(); for( int i = 0; i < dim.length(); i++ ) { if( dim.charAt(i) == '[' ) sb.append( "[" ); } sb.append( getSignatureType(name) ); return( sb.toString() ); } return( getSignatureType( name ) ); } } --- NEW FILE: PeerBindingCodelet.java --- /* * $Id: PeerBindingCodelet.java,v 1.1 2004/08/12 21:11:03 pspeed Exp $ * * Copyright (c) 2004, Paul Speed * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) Neither the names "Progeeks", "JPeerGen", nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.progeeks.jni; import java.util.*; import com.sun.javadoc.*; /** * A codelet that generates the .h file that is included * in the C++ peer interface file. The peer binding * contains the method declarations to match the JNI * C methods. * * @version $Revision: 1.1 $ * @author Paul Speed */ public class PeerBindingCodelet extends JniCodelet { private List peerMethods = new ArrayList(); /** * Initialized the codelet to work with the specified * root doc structure. */ public void initialize( RootDoc root, Properties settings ) { super.initialize( root, settings ); } /** * Writes out the beginning of the file. */ protected void writeHeader() { // Clear the method cache peerMethods.clear(); beginComment(); printLine( "**** DO NOT EDIT THIS FILE - it is machine generated ***" ); printLine( "This file is include in the actual peer interface file" ); printLine( "and contains the bindings for the JNI stub to call." ); endComment(); String fullName = NameUtils.mangle(currentClass.qualifiedName()); beginComment(); printLine( "Binding definitions header for class " + fullName ); endComment(); newLine(); printLine( "#ifndef _Included_Binding_" + fullName ); printLine( "#define _Included_Binding_" + fullName ); } /** * Writes out the end of the file. */ protected void writeFooter() { writeSupportMethods(); writePrivateSection(); printLine( "#endif" ); } /** * Returns the file name that should be used to generate * the specified class based on the specific codelet * implementation. */ public String getFileName( ClassDoc classDoc ) { return( NameUtils.bindingFileName( classDoc ) ); } /** * Writes out the generated code for the specified method * definition based on the specific codelet implementation. */ public void writeMethod( MethodDoc m ) { if( m.isNative() ) { printComment( m ); if( m.isStatic() ) printBegin( "static " ); else printBegin( "" ); printMid( getJniType(m.returnType()) ); printMid( " " ); printMid( NameUtils.peerMethodName(m.name(), m.containingClass().qualifiedName()) ); if( m.isStatic() ) { printMid( "(JNIEnv *env, jclass cls" ); } else { printMid( "(JNIEnv *env, jobject self" ); } // Print out the rest of the parameters boolean castableTypes = isCastableType(m.returnType()); Parameter[] parms = m.parameters(); for( int i = 0; i < parms.length; i++ ) { printMid( ", " ); printMid( getJniType(parms[i].type()) ); printMid( " " ); printMid( parms[i].name() ); if( !isCastableType(parms[i].type()) ) castableTypes = false; } if( !castableTypes ) { printEnd( ");" ); } else { printEnd( ")" ); writeBoundMethodCode( m ); } newLine(); } else { boolean peered = false; // See if it has a "reverse" tag. Tag[] tags = m.tags(); for( int i = 0; i < tags.length; i++ ) { if( PeerDoclet.TAG_PEERED.equals(tags[i].name()) ) peered = true; } if( peered ) generateReverseBinding( m ); } } /** * Writes out a C++ method declaration as well as an * inline JNI->C++ mapping to match the C binding. * This is used when the JNI types can be automapped * to C++ types. */ protected void writeBoundMethodCode( MethodDoc m ) { // We can give it a default method body that // calls the bound method declaration. beginSection(); String retType = getCType(m.returnType()); boolean hasReturnType = false; if( retType != null && !"void".equals(retType) ) { hasReturnType = true; printBegin( "return " ); // Need to do any conversion needed to change the C type // to the Java type. printMid( convertValueToJniStart( m.returnType() ) ); } else { printBegin( "" ); } if( m.isStatic() ) printMid( NameUtils.peerClassName(m.containingClass()) + "::" ); else printMid( "" ); printMid( m.name() ); printMid( "(" ); Parameter[] parms = m.parameters(); for( int i = 0; i < parms.length; i++ ) { if( i > 0 ) printMid( ", " ); printMid( convertValueToC(parms[i].type(), parms[i].name()) ); } printMid( ")" ); if( hasReturnType ) printMid( convertValueToJniEnd( m.returnType() ) ); printEnd( ";" ); endSection(); // Now write the native method declaration that doesn't // have all of the JNI garbage if( m.isStatic() ) printBegin( "static " ); else printBegin( "" ); printMid( getCType(m.returnType()) ); printMid( " " ); printMid( m.name() ); printMid( "(" ); for( int i = 0; i < parms.length; i++ ) { if( i > 0 ) printMid( ", " ); printMid( getCType(parms[i].type()) ); printMid( " " ); printMid( parms[i].name() ); } printEnd( ");" ); } protected void writeStaticMethodHeader( MethodDoc m ) { printLine( "static jclass javaClass = env->FindClass( \"" + currentClass.qualifiedName().replace( '.', '/' ) + "\" );" ); printBegin( "static jmethodID mid_" ); printMid( m.name() ); printMid( " = env->GetStaticMethodID( javaClass, \"" ); printMid( m.name() ); printMid( "\", \"" ); printMid( NameUtils.getSignature( m ) ); printEnd( "\" );" ); } protected void generateReverseBinding( MethodDoc m ) { Parameter[] parms = m.parameters(); // Add the method to the cache peerMethods.add(m); // Generate an inlined method for calling the Java method // from C++ newLine(); printComment( m, true ); if( m.isStatic() ) printBegin( "static " ); else printBegin( "" ); if( isCastableType(m.returnType()) ) printMid( getCType(m.returnType()) ); else printMid( getJniType(m.returnType()) ); printMid( " " ); printMid( m.name() ); printMid( "(" ); for( int i = 0; i < parms.length; i++ ) { if( i > 0 ) printMid( ", " ); if( isCastableType(parms[i].type()) ) { printMid( getSimpleCType(parms[i].type()) ); } else { System.out.println( "Warning: non-castable type:" + parms[i].type() ); printMid( getJniType(parms[i].type()) ); } printMid( " " ); printMid( parms[i].name() ); } printEnd( ")" ); beginSection(); printLine( "JNIEnv *env = JniUtils::attachThread();" ); if( m.isStatic() ) { writeStaticMethodHeader( m ); } if( "void".equals(m.returnType().typeName()) ) { if( m.isStatic() ) printBegin( "env->CallStaticVoidMethod( javaClass, mid_" ); else printBegin( "env->CallVoidMethod( javaPeer, mid_" ); printMid( m.name() ); for( int i = 0; i < parms.length; i++ ) { printMid( ", " ); if( isCastableType(parms[i].type()) ) { TypeConverter conv = getConverter( getSimpleCType(parms[i].type()), getJniType(parms[i].type()) ); printMid( conv.getConvertedValue( parms[i].name() ) ); } else { printMid( parms[i].name() ); } } printEnd( " );" ); } else { StringBuffer returnVal = new StringBuffer( "env->" ); returnVal.append( getJniMethodCall( m ) ); if( m.isStatic() ) returnVal.append( "( javaClass, mid_" ); else returnVal.append( "( javaPeer, mid_" ); returnVal.append( m.name() ); for( int i = 0; i < parms.length; i++ ) { returnVal.append( ", " ); if( isCastableType(parms[i].type()) ) { TypeConverter conv = getConverter( getSimpleCType(parms[i].type()), getJniType(parms[i].type()) ); returnVal.append( conv.getConvertedValue( parms[i].name() ) ); } else { returnVal.append( parms[i].name() ); } } returnVal.append( " )" ); printBegin( "return " ); if( isCastableTy... [truncated message content] |