src/jdk.rmic/share/classes/sun/rmi/rmic/newrmic/jrmp/StubSkeletonWriter.java
changeset 47519 b1f360639517
parent 47518 783d04ecccc3
parent 47496 66e2e3f62eb5
child 47701 be620a591379
child 47716 c9181704b389
child 47803 2cd7d700217f
equal deleted inserted replaced
47518:783d04ecccc3 47519:b1f360639517
     1 /*
       
     2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.rmi.rmic.newrmic.jrmp;
       
    27 
       
    28 import com.sun.javadoc.ClassDoc;
       
    29 import com.sun.javadoc.MethodDoc;
       
    30 import com.sun.javadoc.Type;
       
    31 import java.io.IOException;
       
    32 import java.util.ArrayList;
       
    33 import java.util.Iterator;
       
    34 import java.util.List;
       
    35 import sun.rmi.rmic.newrmic.BatchEnvironment;
       
    36 import sun.rmi.rmic.newrmic.IndentingWriter;
       
    37 
       
    38 import static sun.rmi.rmic.newrmic.Constants.*;
       
    39 import static sun.rmi.rmic.newrmic.jrmp.Constants.*;
       
    40 
       
    41 /**
       
    42  * Writes the source code for the stub class and (optionally) skeleton
       
    43  * class for a particular remote implementation class.
       
    44  *
       
    45  * WARNING: The contents of this source file are not part of any
       
    46  * supported API.  Code that depends on them does so at its own risk:
       
    47  * they are subject to change or removal without notice.
       
    48  *
       
    49  * @author Peter Jones
       
    50  **/
       
    51 class StubSkeletonWriter {
       
    52 
       
    53     /** rmic environment for this object */
       
    54     private final BatchEnvironment env;
       
    55 
       
    56     /** the remote implementation class to generate code for */
       
    57     private final RemoteClass remoteClass;
       
    58 
       
    59     /** version of the JRMP stub protocol to generate code for */
       
    60     private final StubVersion version;
       
    61 
       
    62     /*
       
    63      * binary names of the stub and skeleton classes to generate for
       
    64      * the remote class
       
    65      */
       
    66     private final String stubClassName;
       
    67     private final String skeletonClassName;
       
    68 
       
    69     /* package name and simple names of the stub and skeleton classes */
       
    70     private final String packageName;
       
    71     private final String stubClassSimpleName;
       
    72     private final String skeletonClassSimpleName;
       
    73 
       
    74     /** remote methods of class, indexed by operation number */
       
    75     private final RemoteClass.Method[] remoteMethods;
       
    76 
       
    77     /**
       
    78      * Names to use for the java.lang.reflect.Method static fields in
       
    79      * the generated stub class corresponding to each remote method.
       
    80      **/
       
    81     private final String[] methodFieldNames;
       
    82 
       
    83     /**
       
    84      * Creates a StubSkeletonWriter instance for the specified remote
       
    85      * implementation class.  The generated code will implement the
       
    86      * specified JRMP stub protocol version.
       
    87      **/
       
    88     StubSkeletonWriter(BatchEnvironment env,
       
    89                        RemoteClass remoteClass,
       
    90                        StubVersion version)
       
    91     {
       
    92         this.env = env;
       
    93         this.remoteClass = remoteClass;
       
    94         this.version = version;
       
    95 
       
    96         stubClassName = Util.binaryNameOf(remoteClass.classDoc()) + "_Stub";
       
    97         skeletonClassName =
       
    98             Util.binaryNameOf(remoteClass.classDoc()) + "_Skel";
       
    99 
       
   100         int i = stubClassName.lastIndexOf('.');
       
   101         packageName = (i != -1 ? stubClassName.substring(0, i) : "");
       
   102         stubClassSimpleName = stubClassName.substring(i + 1);
       
   103         skeletonClassSimpleName = skeletonClassName.substring(i + 1);
       
   104 
       
   105         remoteMethods = remoteClass.remoteMethods();
       
   106         methodFieldNames = nameMethodFields(remoteMethods);
       
   107     }
       
   108 
       
   109     /**
       
   110      * Returns the binary name of the stub class to generate for the
       
   111      * remote implementation class.
       
   112      **/
       
   113     String stubClassName() {
       
   114         return stubClassName;
       
   115     }
       
   116 
       
   117     /**
       
   118      * Returns the binary name of the skeleton class to generate for
       
   119      * the remote implementation class.
       
   120      **/
       
   121     String skeletonClassName() {
       
   122         return skeletonClassName;
       
   123     }
       
   124 
       
   125     /**
       
   126      * Writes the stub class for the remote class to a stream.
       
   127      **/
       
   128     void writeStub(IndentingWriter p) throws IOException {
       
   129 
       
   130         /*
       
   131          * Write boiler plate comment.
       
   132          */
       
   133         p.pln("// Stub class generated by rmic, do not edit.");
       
   134         p.pln("// Contents subject to change without notice.");
       
   135         p.pln();
       
   136 
       
   137         /*
       
   138          * If remote implementation class was in a particular package,
       
   139          * declare the stub class to be in the same package.
       
   140          */
       
   141         if (!packageName.equals("")) {
       
   142             p.pln("package " + packageName + ";");
       
   143             p.pln();
       
   144         }
       
   145 
       
   146         /*
       
   147          * Declare the stub class; implement all remote interfaces.
       
   148          */
       
   149         p.plnI("public final class " + stubClassSimpleName);
       
   150         p.pln("extends " + REMOTE_STUB);
       
   151         ClassDoc[] remoteInterfaces = remoteClass.remoteInterfaces();
       
   152         if (remoteInterfaces.length > 0) {
       
   153             p.p("implements ");
       
   154             for (int i = 0; i < remoteInterfaces.length; i++) {
       
   155                 if (i > 0) {
       
   156                     p.p(", ");
       
   157                 }
       
   158                 p.p(remoteInterfaces[i].qualifiedName());
       
   159             }
       
   160             p.pln();
       
   161         }
       
   162         p.pOlnI("{");
       
   163 
       
   164         if (version == StubVersion.V1_1 ||
       
   165             version == StubVersion.VCOMPAT)
       
   166         {
       
   167             writeOperationsArray(p);
       
   168             p.pln();
       
   169             writeInterfaceHash(p);
       
   170             p.pln();
       
   171         }
       
   172 
       
   173         if (version == StubVersion.VCOMPAT ||
       
   174             version == StubVersion.V1_2)
       
   175         {
       
   176             p.pln("private static final long serialVersionUID = " +
       
   177                 STUB_SERIAL_VERSION_UID + ";");
       
   178             p.pln();
       
   179 
       
   180             /*
       
   181              * We only need to declare and initialize the static fields of
       
   182              * Method objects for each remote method if there are any remote
       
   183              * methods; otherwise, skip this code entirely, to avoid generating
       
   184              * a try/catch block for a checked exception that cannot occur
       
   185              * (see bugid 4125181).
       
   186              */
       
   187             if (methodFieldNames.length > 0) {
       
   188                 if (version == StubVersion.VCOMPAT) {
       
   189                     p.pln("private static boolean useNewInvoke;");
       
   190                 }
       
   191                 writeMethodFieldDeclarations(p);
       
   192                 p.pln();
       
   193 
       
   194                 /*
       
   195                  * Initialize java.lang.reflect.Method fields for each remote
       
   196                  * method in a static initializer.
       
   197                  */
       
   198                 p.plnI("static {");
       
   199                 p.plnI("try {");
       
   200                 if (version == StubVersion.VCOMPAT) {
       
   201                     /*
       
   202                      * Fat stubs must determine whether the API required for
       
   203                      * the JDK 1.2 stub protocol is supported in the current
       
   204                      * runtime, so that it can use it if supported.  This is
       
   205                      * determined by using the Reflection API to test if the
       
   206                      * new invoke method on RemoteRef exists, and setting the
       
   207                      * static boolean "useNewInvoke" to true if it does, or
       
   208                      * to false if a NoSuchMethodException is thrown.
       
   209                      */
       
   210                     p.plnI(REMOTE_REF + ".class.getMethod(\"invoke\",");
       
   211                     p.plnI("new java.lang.Class[] {");
       
   212                     p.pln(REMOTE + ".class,");
       
   213                     p.pln("java.lang.reflect.Method.class,");
       
   214                     p.pln("java.lang.Object[].class,");
       
   215                     p.pln("long.class");
       
   216                     p.pOln("});");
       
   217                     p.pO();
       
   218                     p.pln("useNewInvoke = true;");
       
   219                 }
       
   220                 writeMethodFieldInitializers(p);
       
   221                 p.pOlnI("} catch (java.lang.NoSuchMethodException e) {");
       
   222                 if (version == StubVersion.VCOMPAT) {
       
   223                     p.pln("useNewInvoke = false;");
       
   224                 } else {
       
   225                     p.plnI("throw new java.lang.NoSuchMethodError(");
       
   226                     p.pln("\"stub class initialization failed\");");
       
   227                     p.pO();
       
   228                 }
       
   229                 p.pOln("}");            // end try/catch block
       
   230                 p.pOln("}");            // end static initializer
       
   231                 p.pln();
       
   232             }
       
   233         }
       
   234 
       
   235         writeStubConstructors(p);
       
   236         p.pln();
       
   237 
       
   238         /*
       
   239          * Write each stub method.
       
   240          */
       
   241         if (remoteMethods.length > 0) {
       
   242             p.pln("// methods from remote interfaces");
       
   243             for (int i = 0; i < remoteMethods.length; ++i) {
       
   244                 p.pln();
       
   245                 writeStubMethod(p, i);
       
   246             }
       
   247         }
       
   248 
       
   249         p.pOln("}");                    // end stub class
       
   250     }
       
   251 
       
   252     /**
       
   253      * Writes the constructors for the stub class.
       
   254      **/
       
   255     private void writeStubConstructors(IndentingWriter p)
       
   256         throws IOException
       
   257     {
       
   258         p.pln("// constructors");
       
   259 
       
   260         /*
       
   261          * Only stubs compatible with the JDK 1.1 stub protocol need
       
   262          * a no-arg constructor; later versions use reflection to find
       
   263          * the constructor that directly takes a RemoteRef argument.
       
   264          */
       
   265         if (version == StubVersion.V1_1 ||
       
   266             version == StubVersion.VCOMPAT)
       
   267         {
       
   268             p.plnI("public " + stubClassSimpleName + "() {");
       
   269             p.pln("super();");
       
   270             p.pOln("}");
       
   271         }
       
   272 
       
   273         p.plnI("public " + stubClassSimpleName + "(" + REMOTE_REF + " ref) {");
       
   274         p.pln("super(ref);");
       
   275         p.pOln("}");
       
   276     }
       
   277 
       
   278     /**
       
   279      * Writes the stub method for the remote method with the given
       
   280      * operation number.
       
   281      **/
       
   282     private void writeStubMethod(IndentingWriter p, int opnum)
       
   283         throws IOException
       
   284     {
       
   285         RemoteClass.Method method = remoteMethods[opnum];
       
   286         MethodDoc methodDoc = method.methodDoc();
       
   287         String methodName = methodDoc.name();
       
   288         Type[] paramTypes = method.parameterTypes();
       
   289         String paramNames[] = nameParameters(paramTypes);
       
   290         Type returnType = methodDoc.returnType();
       
   291         ClassDoc[] exceptions = method.exceptionTypes();
       
   292 
       
   293         /*
       
   294          * Declare stub method; throw exceptions declared in remote
       
   295          * interface(s).
       
   296          */
       
   297         p.pln("// implementation of " +
       
   298               Util.getFriendlyUnqualifiedSignature(methodDoc));
       
   299         p.p("public " + returnType.toString() + " " + methodName + "(");
       
   300         for (int i = 0; i < paramTypes.length; i++) {
       
   301             if (i > 0) {
       
   302                 p.p(", ");
       
   303             }
       
   304             p.p(paramTypes[i].toString() + " " + paramNames[i]);
       
   305         }
       
   306         p.plnI(")");
       
   307         if (exceptions.length > 0) {
       
   308             p.p("throws ");
       
   309             for (int i = 0; i < exceptions.length; i++) {
       
   310                 if (i > 0) {
       
   311                     p.p(", ");
       
   312                 }
       
   313                 p.p(exceptions[i].qualifiedName());
       
   314             }
       
   315             p.pln();
       
   316         }
       
   317         p.pOlnI("{");
       
   318 
       
   319         /*
       
   320          * The RemoteRef.invoke methods throw Exception, but unless
       
   321          * this stub method throws Exception as well, we must catch
       
   322          * Exceptions thrown from the invocation.  So we must catch
       
   323          * Exception and rethrow something we can throw:
       
   324          * UnexpectedException, which is a subclass of
       
   325          * RemoteException.  But for any subclasses of Exception that
       
   326          * we can throw, like RemoteException, RuntimeException, and
       
   327          * any of the exceptions declared by this stub method, we want
       
   328          * them to pass through unmodified, so first we must catch any
       
   329          * such exceptions and rethrow them directly.
       
   330          *
       
   331          * We have to be careful generating the rethrowing catch
       
   332          * blocks here, because javac will flag an error if there are
       
   333          * any unreachable catch blocks, i.e. if the catch of an
       
   334          * exception class follows a previous catch of it or of one of
       
   335          * its superclasses.  The following method invocation takes
       
   336          * care of these details.
       
   337          */
       
   338         List<ClassDoc> catchList = computeUniqueCatchList(exceptions);
       
   339 
       
   340         /*
       
   341          * If we need to catch any particular exceptions (i.e. this method
       
   342          * does not declare java.lang.Exception), put the entire stub
       
   343          * method in a try block.
       
   344          */
       
   345         if (catchList.size() > 0) {
       
   346             p.plnI("try {");
       
   347         }
       
   348 
       
   349         if (version == StubVersion.VCOMPAT) {
       
   350             p.plnI("if (useNewInvoke) {");
       
   351         }
       
   352         if (version == StubVersion.VCOMPAT ||
       
   353             version == StubVersion.V1_2)
       
   354         {
       
   355             if (!Util.isVoid(returnType)) {
       
   356                 p.p("Object $result = ");               // REMIND: why $?
       
   357             }
       
   358             p.p("ref.invoke(this, " + methodFieldNames[opnum] + ", ");
       
   359             if (paramTypes.length > 0) {
       
   360                 p.p("new java.lang.Object[] {");
       
   361                 for (int i = 0; i < paramTypes.length; i++) {
       
   362                     if (i > 0)
       
   363                         p.p(", ");
       
   364                     p.p(wrapArgumentCode(paramTypes[i], paramNames[i]));
       
   365                 }
       
   366                 p.p("}");
       
   367             } else {
       
   368                 p.p("null");
       
   369             }
       
   370             p.pln(", " + method.methodHash() + "L);");
       
   371             if (!Util.isVoid(returnType)) {
       
   372                 p.pln("return " +
       
   373                     unwrapArgumentCode(returnType, "$result") + ";");
       
   374             }
       
   375         }
       
   376         if (version == StubVersion.VCOMPAT) {
       
   377             p.pOlnI("} else {");
       
   378         }
       
   379         if (version == StubVersion.V1_1 ||
       
   380             version == StubVersion.VCOMPAT)
       
   381         {
       
   382             p.pln(REMOTE_CALL + " call = ref.newCall((" + REMOTE_OBJECT +
       
   383                 ") this, operations, " + opnum + ", interfaceHash);");
       
   384 
       
   385             if (paramTypes.length > 0) {
       
   386                 p.plnI("try {");
       
   387                 p.pln("java.io.ObjectOutput out = call.getOutputStream();");
       
   388                 writeMarshalArguments(p, "out", paramTypes, paramNames);
       
   389                 p.pOlnI("} catch (java.io.IOException e) {");
       
   390                 p.pln("throw new " + MARSHAL_EXCEPTION +
       
   391                     "(\"error marshalling arguments\", e);");
       
   392                 p.pOln("}");
       
   393             }
       
   394 
       
   395             p.pln("ref.invoke(call);");
       
   396 
       
   397             if (Util.isVoid(returnType)) {
       
   398                 p.pln("ref.done(call);");
       
   399             } else {
       
   400                 p.pln(returnType.toString() + " $result;");
       
   401                                                         // REMIND: why $?
       
   402                 p.plnI("try {");
       
   403                 p.pln("java.io.ObjectInput in = call.getInputStream();");
       
   404                 boolean objectRead =
       
   405                     writeUnmarshalArgument(p, "in", returnType, "$result");
       
   406                 p.pln(";");
       
   407                 p.pOlnI("} catch (java.io.IOException e) {");
       
   408                 p.pln("throw new " + UNMARSHAL_EXCEPTION +
       
   409                     "(\"error unmarshalling return\", e);");
       
   410                 /*
       
   411                  * If any only if readObject has been invoked, we must catch
       
   412                  * ClassNotFoundException as well as IOException.
       
   413                  */
       
   414                 if (objectRead) {
       
   415                     p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");
       
   416                     p.pln("throw new " + UNMARSHAL_EXCEPTION +
       
   417                         "(\"error unmarshalling return\", e);");
       
   418                 }
       
   419                 p.pOlnI("} finally {");
       
   420                 p.pln("ref.done(call);");
       
   421                 p.pOln("}");
       
   422                 p.pln("return $result;");
       
   423             }
       
   424         }
       
   425         if (version == StubVersion.VCOMPAT) {
       
   426             p.pOln("}");                // end if/else (useNewInvoke) block
       
   427         }
       
   428 
       
   429         /*
       
   430          * If we need to catch any particular exceptions, finally write
       
   431          * the catch blocks for them, rethrow any other Exceptions with an
       
   432          * UnexpectedException, and end the try block.
       
   433          */
       
   434         if (catchList.size() > 0) {
       
   435             for (ClassDoc catchClass : catchList) {
       
   436                 p.pOlnI("} catch (" + catchClass.qualifiedName() + " e) {");
       
   437                 p.pln("throw e;");
       
   438             }
       
   439             p.pOlnI("} catch (java.lang.Exception e) {");
       
   440             p.pln("throw new " + UNEXPECTED_EXCEPTION +
       
   441                 "(\"undeclared checked exception\", e);");
       
   442             p.pOln("}");                // end try/catch block
       
   443         }
       
   444 
       
   445         p.pOln("}");                    // end stub method
       
   446     }
       
   447 
       
   448     /**
       
   449      * Computes the exceptions that need to be caught and rethrown in
       
   450      * a stub method before wrapping Exceptions in
       
   451      * UnexpectedExceptions, given the exceptions declared in the
       
   452      * throws clause of the method.  Returns a list containing the
       
   453      * exception to catch.  Each exception is guaranteed to be unique,
       
   454      * i.e. not a subclass of any of the other exceptions in the list,
       
   455      * so the catch blocks for these exceptions may be generated in
       
   456      * any order relative to each other.
       
   457      *
       
   458      * RemoteException and RuntimeException are each automatically
       
   459      * placed in the returned list (unless any of their superclasses
       
   460      * are already present), since those exceptions should always be
       
   461      * directly rethrown by a stub method.
       
   462      *
       
   463      * The returned list will be empty if java.lang.Exception or one
       
   464      * of its superclasses is in the throws clause of the method,
       
   465      * indicating that no exceptions need to be caught.
       
   466      **/
       
   467     private List<ClassDoc> computeUniqueCatchList(ClassDoc[] exceptions) {
       
   468         List<ClassDoc> uniqueList = new ArrayList<ClassDoc>();
       
   469 
       
   470         uniqueList.add(env.docRuntimeException());
       
   471         uniqueList.add(env.docRemoteException()); // always catch/rethrow these
       
   472 
       
   473         /* For each exception declared by the stub method's throws clause: */
       
   474     nextException:
       
   475         for (ClassDoc ex : exceptions) {
       
   476             if (env.docException().subclassOf(ex)) {
       
   477                 /*
       
   478                  * If java.lang.Exception (or a superclass) was declared
       
   479                  * in the throws clause of this stub method, then we don't
       
   480                  * have to bother catching anything; clear the list and
       
   481                  * return.
       
   482                  */
       
   483                 uniqueList.clear();
       
   484                 break;
       
   485             } else if (!ex.subclassOf(env.docException())) {
       
   486                 /*
       
   487                  * Ignore other Throwables that do not extend Exception,
       
   488                  * because they cannot be thrown by the invoke methods.
       
   489                  */
       
   490                 continue;
       
   491             }
       
   492             /*
       
   493              * Compare this exception against the current list of
       
   494              * exceptions that need to be caught:
       
   495              */
       
   496             for (Iterator<ClassDoc> i = uniqueList.iterator(); i.hasNext();) {
       
   497                 ClassDoc ex2 = i.next();
       
   498                 if (ex.subclassOf(ex2)) {
       
   499                     /*
       
   500                      * If a superclass of this exception is already on
       
   501                      * the list to catch, then ignore this one and continue;
       
   502                      */
       
   503                     continue nextException;
       
   504                 } else if (ex2.subclassOf(ex)) {
       
   505                     /*
       
   506                      * If a subclass of this exception is on the list
       
   507                      * to catch, then remove it;
       
   508                      */
       
   509                     i.remove();
       
   510                 }
       
   511             }
       
   512             /* This exception is unique: add it to the list to catch. */
       
   513             uniqueList.add(ex);
       
   514         }
       
   515         return uniqueList;
       
   516     }
       
   517 
       
   518     /**
       
   519      * Writes the skeleton for the remote class to a stream.
       
   520      **/
       
   521     void writeSkeleton(IndentingWriter p) throws IOException {
       
   522         if (version == StubVersion.V1_2) {
       
   523             throw new AssertionError(
       
   524                 "should not generate skeleton for version " + version);
       
   525         }
       
   526 
       
   527         /*
       
   528          * Write boiler plate comment.
       
   529          */
       
   530         p.pln("// Skeleton class generated by rmic, do not edit.");
       
   531         p.pln("// Contents subject to change without notice.");
       
   532         p.pln();
       
   533 
       
   534         /*
       
   535          * If remote implementation class was in a particular package,
       
   536          * declare the skeleton class to be in the same package.
       
   537          */
       
   538         if (!packageName.equals("")) {
       
   539             p.pln("package " + packageName + ";");
       
   540             p.pln();
       
   541         }
       
   542 
       
   543         /*
       
   544          * Declare the skeleton class.
       
   545          */
       
   546         p.plnI("public final class " + skeletonClassSimpleName);
       
   547         p.pln("implements " + SKELETON);
       
   548         p.pOlnI("{");
       
   549 
       
   550         writeOperationsArray(p);
       
   551         p.pln();
       
   552 
       
   553         writeInterfaceHash(p);
       
   554         p.pln();
       
   555 
       
   556         /*
       
   557          * Define the getOperations() method.
       
   558          */
       
   559         p.plnI("public " + OPERATION + "[] getOperations() {");
       
   560         p.pln("return (" + OPERATION + "[]) operations.clone();");
       
   561         p.pOln("}");
       
   562         p.pln();
       
   563 
       
   564         /*
       
   565          * Define the dispatch() method.
       
   566          */
       
   567         p.plnI("public void dispatch(" + REMOTE + " obj, " +
       
   568             REMOTE_CALL + " call, int opnum, long hash)");
       
   569         p.pln("throws java.lang.Exception");
       
   570         p.pOlnI("{");
       
   571 
       
   572         if (version == StubVersion.VCOMPAT) {
       
   573             p.plnI("if (opnum < 0) {");
       
   574             if (remoteMethods.length > 0) {
       
   575                 for (int opnum = 0; opnum < remoteMethods.length; opnum++) {
       
   576                     if (opnum > 0)
       
   577                         p.pO("} else ");
       
   578                     p.plnI("if (hash == " +
       
   579                         remoteMethods[opnum].methodHash() + "L) {");
       
   580                     p.pln("opnum = " + opnum + ";");
       
   581                 }
       
   582                 p.pOlnI("} else {");
       
   583             }
       
   584             /*
       
   585              * Skeleton throws UnmarshalException if it does not recognize
       
   586              * the method hash; this is what UnicastServerRef.dispatch()
       
   587              * would do.
       
   588              */
       
   589             p.pln("throw new " +
       
   590                 UNMARSHAL_EXCEPTION + "(\"invalid method hash\");");
       
   591             if (remoteMethods.length > 0) {
       
   592                 p.pOln("}");
       
   593             }
       
   594             /*
       
   595              * Ignore the validation of the interface hash if the
       
   596              * operation number was negative, since it is really a
       
   597              * method hash instead.
       
   598              */
       
   599             p.pOlnI("} else {");
       
   600         }
       
   601 
       
   602         p.plnI("if (hash != interfaceHash)");
       
   603         p.pln("throw new " +
       
   604             SKELETON_MISMATCH_EXCEPTION + "(\"interface hash mismatch\");");
       
   605         p.pO();
       
   606 
       
   607         if (version == StubVersion.VCOMPAT) {
       
   608             p.pOln("}");                // end if/else (opnum < 0) block
       
   609         }
       
   610         p.pln();
       
   611 
       
   612         /*
       
   613          * Cast remote object reference to the remote implementation
       
   614          * class, if it's not private.  We don't use the binary name
       
   615          * of the class like previous implementations did because that
       
   616          * would not compile with javac (since 1.4.1).  If the remote
       
   617          * implementation class is private, then we can't cast to it
       
   618          * like previous implementations did because that also would
       
   619          * not compile with javac-- so instead, we'll have to try to
       
   620          * cast to the remote interface for each remote method.
       
   621          */
       
   622         if (!remoteClass.classDoc().isPrivate()) {
       
   623             p.pln(remoteClass.classDoc().qualifiedName() + " server = (" +
       
   624                   remoteClass.classDoc().qualifiedName() + ") obj;");
       
   625         }
       
   626 
       
   627         /*
       
   628          * Process call according to the operation number.
       
   629          */
       
   630         p.plnI("switch (opnum) {");
       
   631         for (int opnum = 0; opnum < remoteMethods.length; opnum++) {
       
   632             writeSkeletonDispatchCase(p, opnum);
       
   633         }
       
   634         p.pOlnI("default:");
       
   635         /*
       
   636          * Skeleton throws UnmarshalException if it does not recognize
       
   637          * the operation number; this is consistent with the case of an
       
   638          * unrecognized method hash.
       
   639          */
       
   640         p.pln("throw new " + UNMARSHAL_EXCEPTION +
       
   641             "(\"invalid method number\");");
       
   642         p.pOln("}");                    // end switch statement
       
   643 
       
   644         p.pOln("}");                    // end dispatch() method
       
   645 
       
   646         p.pOln("}");                    // end skeleton class
       
   647     }
       
   648 
       
   649     /**
       
   650      * Writes the case block for the skeleton's dispatch method for
       
   651      * the remote method with the given "opnum".
       
   652      **/
       
   653     private void writeSkeletonDispatchCase(IndentingWriter p, int opnum)
       
   654         throws IOException
       
   655     {
       
   656         RemoteClass.Method method = remoteMethods[opnum];
       
   657         MethodDoc methodDoc = method.methodDoc();
       
   658         String methodName = methodDoc.name();
       
   659         Type paramTypes[] = method.parameterTypes();
       
   660         String paramNames[] = nameParameters(paramTypes);
       
   661         Type returnType = methodDoc.returnType();
       
   662 
       
   663         p.pOlnI("case " + opnum + ": // " +
       
   664             Util.getFriendlyUnqualifiedSignature(methodDoc));
       
   665         /*
       
   666          * Use nested block statement inside case to provide an independent
       
   667          * namespace for local variables used to unmarshal parameters for
       
   668          * this remote method.
       
   669          */
       
   670         p.pOlnI("{");
       
   671 
       
   672         if (paramTypes.length > 0) {
       
   673             /*
       
   674              * Declare local variables to hold arguments.
       
   675              */
       
   676             for (int i = 0; i < paramTypes.length; i++) {
       
   677                 p.pln(paramTypes[i].toString() + " " + paramNames[i] + ";");
       
   678             }
       
   679 
       
   680             /*
       
   681              * Unmarshal arguments from call stream.
       
   682              */
       
   683             p.plnI("try {");
       
   684             p.pln("java.io.ObjectInput in = call.getInputStream();");
       
   685             boolean objectsRead = writeUnmarshalArguments(p, "in",
       
   686                 paramTypes, paramNames);
       
   687             p.pOlnI("} catch (java.io.IOException e) {");
       
   688             p.pln("throw new " + UNMARSHAL_EXCEPTION +
       
   689                 "(\"error unmarshalling arguments\", e);");
       
   690             /*
       
   691              * If any only if readObject has been invoked, we must catch
       
   692              * ClassNotFoundException as well as IOException.
       
   693              */
       
   694             if (objectsRead) {
       
   695                 p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");
       
   696                 p.pln("throw new " + UNMARSHAL_EXCEPTION +
       
   697                     "(\"error unmarshalling arguments\", e);");
       
   698             }
       
   699             p.pOlnI("} finally {");
       
   700             p.pln("call.releaseInputStream();");
       
   701             p.pOln("}");
       
   702         } else {
       
   703             p.pln("call.releaseInputStream();");
       
   704         }
       
   705 
       
   706         if (!Util.isVoid(returnType)) {
       
   707             /*
       
   708              * Declare variable to hold return type, if not void.
       
   709              */
       
   710             p.p(returnType.toString() + " $result = ");
       
   711                                                         // REMIND: why $?
       
   712         }
       
   713 
       
   714         /*
       
   715          * Invoke the method on the server object.  If the remote
       
   716          * implementation class is private, then we don't have a
       
   717          * reference cast to it, and so we try to cast to the remote
       
   718          * object reference to the method's declaring interface here.
       
   719          */
       
   720         String target = remoteClass.classDoc().isPrivate() ?
       
   721             "((" + methodDoc.containingClass().qualifiedName() + ") obj)" :
       
   722             "server";
       
   723         p.p(target + "." + methodName + "(");
       
   724         for (int i = 0; i < paramNames.length; i++) {
       
   725             if (i > 0)
       
   726                 p.p(", ");
       
   727             p.p(paramNames[i]);
       
   728         }
       
   729         p.pln(");");
       
   730 
       
   731         /*
       
   732          * Always invoke getResultStream(true) on the call object to send
       
   733          * the indication of a successful invocation to the caller.  If
       
   734          * the return type is not void, keep the result stream and marshal
       
   735          * the return value.
       
   736          */
       
   737         p.plnI("try {");
       
   738         if (!Util.isVoid(returnType)) {
       
   739             p.p("java.io.ObjectOutput out = ");
       
   740         }
       
   741         p.pln("call.getResultStream(true);");
       
   742         if (!Util.isVoid(returnType)) {
       
   743             writeMarshalArgument(p, "out", returnType, "$result");
       
   744             p.pln(";");
       
   745         }
       
   746         p.pOlnI("} catch (java.io.IOException e) {");
       
   747         p.pln("throw new " +
       
   748             MARSHAL_EXCEPTION + "(\"error marshalling return\", e);");
       
   749         p.pOln("}");
       
   750 
       
   751         p.pln("break;");                // break from switch statement
       
   752 
       
   753         p.pOlnI("}");                   // end nested block statement
       
   754         p.pln();
       
   755     }
       
   756 
       
   757     /**
       
   758      * Writes declaration and initializer for "operations" static array.
       
   759      **/
       
   760     private void writeOperationsArray(IndentingWriter p)
       
   761         throws IOException
       
   762     {
       
   763         p.plnI("private static final " + OPERATION + "[] operations = {");
       
   764         for (int i = 0; i < remoteMethods.length; i++) {
       
   765             if (i > 0)
       
   766                 p.pln(",");
       
   767             p.p("new " + OPERATION + "(\"" +
       
   768                 remoteMethods[i].operationString() + "\")");
       
   769         }
       
   770         p.pln();
       
   771         p.pOln("};");
       
   772     }
       
   773 
       
   774     /**
       
   775      * Writes declaration and initializer for "interfaceHash" static field.
       
   776      **/
       
   777     private void writeInterfaceHash(IndentingWriter p)
       
   778         throws IOException
       
   779     {
       
   780         p.pln("private static final long interfaceHash = " +
       
   781             remoteClass.interfaceHash() + "L;");
       
   782     }
       
   783 
       
   784     /**
       
   785      * Writes declaration for java.lang.reflect.Method static fields
       
   786      * corresponding to each remote method in a stub.
       
   787      **/
       
   788     private void writeMethodFieldDeclarations(IndentingWriter p)
       
   789         throws IOException
       
   790     {
       
   791         for (String name : methodFieldNames) {
       
   792             p.pln("private static java.lang.reflect.Method " + name + ";");
       
   793         }
       
   794     }
       
   795 
       
   796     /**
       
   797      * Writes code to initialize the static fields for each method
       
   798      * using the Java Reflection API.
       
   799      **/
       
   800     private void writeMethodFieldInitializers(IndentingWriter p)
       
   801         throws IOException
       
   802     {
       
   803         for (int i = 0; i < methodFieldNames.length; i++) {
       
   804             p.p(methodFieldNames[i] + " = ");
       
   805             /*
       
   806              * Look up the Method object in the somewhat arbitrary
       
   807              * interface that we find in the Method object.
       
   808              */
       
   809             RemoteClass.Method method = remoteMethods[i];
       
   810             MethodDoc methodDoc = method.methodDoc();
       
   811             String methodName = methodDoc.name();
       
   812             Type paramTypes[] = method.parameterTypes();
       
   813 
       
   814             p.p(methodDoc.containingClass().qualifiedName() + ".class.getMethod(\"" +
       
   815                 methodName + "\", new java.lang.Class[] {");
       
   816             for (int j = 0; j < paramTypes.length; j++) {
       
   817                 if (j > 0)
       
   818                     p.p(", ");
       
   819                 p.p(paramTypes[j].toString() + ".class");
       
   820             }
       
   821             p.pln("});");
       
   822         }
       
   823     }
       
   824 
       
   825 
       
   826     /*
       
   827      * Following are a series of static utility methods useful during
       
   828      * the code generation process:
       
   829      */
       
   830 
       
   831     /**
       
   832      * Generates an array of names for fields correspondins to the
       
   833      * given array of remote methods.  Each name in the returned array
       
   834      * is guaranteed to be unique.
       
   835      *
       
   836      * The name of a method is included in its corresponding field
       
   837      * name to enhance readability of the generated code.
       
   838      **/
       
   839     private static String[] nameMethodFields(RemoteClass.Method[] methods) {
       
   840         String[] names = new String[methods.length];
       
   841         for (int i = 0; i < names.length; i++) {
       
   842             names[i] = "$method_" + methods[i].methodDoc().name() + "_" + i;
       
   843         }
       
   844         return names;
       
   845     }
       
   846 
       
   847     /**
       
   848      * Generates an array of names for parameters corresponding to the
       
   849      * given array of types for the parameters.  Each name in the
       
   850      * returned array is guaranteed to be unique.
       
   851      *
       
   852      * A representation of the type of a parameter is included in its
       
   853      * corresponding parameter name to enhance the readability of the
       
   854      * generated code.
       
   855      **/
       
   856     private static String[] nameParameters(Type[] types) {
       
   857         String[] names = new String[types.length];
       
   858         for (int i = 0; i < names.length; i++) {
       
   859             names[i] = "$param_" +
       
   860                 generateNameFromType(types[i]) + "_" + (i + 1);
       
   861         }
       
   862         return names;
       
   863     }
       
   864 
       
   865     /**
       
   866      * Generates a readable string representing the given type
       
   867      * suitable for embedding within a Java identifier.
       
   868      **/
       
   869     private static String generateNameFromType(Type type) {
       
   870         String name = type.typeName().replace('.', '$');
       
   871         int dimensions = type.dimension().length() / 2;
       
   872         for (int i = 0; i < dimensions; i++) {
       
   873             name = "arrayOf_" + name;
       
   874         }
       
   875         return name;
       
   876     }
       
   877 
       
   878     /**
       
   879      * Writes a snippet of Java code to marshal a value named "name"
       
   880      * of type "type" to the java.io.ObjectOutput stream named
       
   881      * "stream".
       
   882      *
       
   883      * Primitive types are marshalled with their corresponding methods
       
   884      * in the java.io.DataOutput interface, and objects (including
       
   885      * arrays) are marshalled using the writeObject method.
       
   886      **/
       
   887     private static void writeMarshalArgument(IndentingWriter p,
       
   888                                              String streamName,
       
   889                                              Type type, String name)
       
   890         throws IOException
       
   891     {
       
   892         if (type.dimension().length() > 0 || type.asClassDoc() != null) {
       
   893             p.p(streamName + ".writeObject(" + name + ")");
       
   894         } else if (type.typeName().equals("boolean")) {
       
   895             p.p(streamName + ".writeBoolean(" + name + ")");
       
   896         } else if (type.typeName().equals("byte")) {
       
   897             p.p(streamName + ".writeByte(" + name + ")");
       
   898         } else if (type.typeName().equals("char")) {
       
   899             p.p(streamName + ".writeChar(" + name + ")");
       
   900         } else if (type.typeName().equals("short")) {
       
   901             p.p(streamName + ".writeShort(" + name + ")");
       
   902         } else if (type.typeName().equals("int")) {
       
   903             p.p(streamName + ".writeInt(" + name + ")");
       
   904         } else if (type.typeName().equals("long")) {
       
   905             p.p(streamName + ".writeLong(" + name + ")");
       
   906         } else if (type.typeName().equals("float")) {
       
   907             p.p(streamName + ".writeFloat(" + name + ")");
       
   908         } else if (type.typeName().equals("double")) {
       
   909             p.p(streamName + ".writeDouble(" + name + ")");
       
   910         } else {
       
   911             throw new AssertionError(type);
       
   912         }
       
   913     }
       
   914 
       
   915     /**
       
   916      * Writes Java statements to marshal a series of values in order
       
   917      * as named in the "names" array, with types as specified in the
       
   918      * "types" array, to the java.io.ObjectOutput stream named
       
   919      * "stream".
       
   920      **/
       
   921     private static void writeMarshalArguments(IndentingWriter p,
       
   922                                               String streamName,
       
   923                                               Type[] types, String[] names)
       
   924         throws IOException
       
   925     {
       
   926         assert types.length == names.length;
       
   927 
       
   928         for (int i = 0; i < types.length; i++) {
       
   929             writeMarshalArgument(p, streamName, types[i], names[i]);
       
   930             p.pln(";");
       
   931         }
       
   932     }
       
   933 
       
   934     /**
       
   935      * Writes a snippet of Java code to unmarshal a value of type
       
   936      * "type" from the java.io.ObjectInput stream named "stream" into
       
   937      * a variable named "name" (if "name" is null, the value is
       
   938      * unmarshalled and discarded).
       
   939      *
       
   940      * Primitive types are unmarshalled with their corresponding
       
   941      * methods in the java.io.DataInput interface, and objects
       
   942      * (including arrays) are unmarshalled using the readObject
       
   943      * method.
       
   944      *
       
   945      * Returns true if code to invoke readObject was written, and
       
   946      * false otherwise.
       
   947      **/
       
   948     private static boolean writeUnmarshalArgument(IndentingWriter p,
       
   949                                                   String streamName,
       
   950                                                   Type type, String name)
       
   951         throws IOException
       
   952     {
       
   953         boolean readObject = false;
       
   954 
       
   955         if (name != null) {
       
   956             p.p(name + " = ");
       
   957         }
       
   958 
       
   959         if (type.dimension().length() > 0 || type.asClassDoc() != null) {
       
   960             p.p("(" + type.toString() + ") " + streamName + ".readObject()");
       
   961             readObject = true;
       
   962         } else if (type.typeName().equals("boolean")) {
       
   963             p.p(streamName + ".readBoolean()");
       
   964         } else if (type.typeName().equals("byte")) {
       
   965             p.p(streamName + ".readByte()");
       
   966         } else if (type.typeName().equals("char")) {
       
   967             p.p(streamName + ".readChar()");
       
   968         } else if (type.typeName().equals("short")) {
       
   969             p.p(streamName + ".readShort()");
       
   970         } else if (type.typeName().equals("int")) {
       
   971             p.p(streamName + ".readInt()");
       
   972         } else if (type.typeName().equals("long")) {
       
   973             p.p(streamName + ".readLong()");
       
   974         } else if (type.typeName().equals("float")) {
       
   975             p.p(streamName + ".readFloat()");
       
   976         } else if (type.typeName().equals("double")) {
       
   977             p.p(streamName + ".readDouble()");
       
   978         } else {
       
   979             throw new AssertionError(type);
       
   980         }
       
   981 
       
   982         return readObject;
       
   983     }
       
   984 
       
   985     /**
       
   986      * Writes Java statements to unmarshal a series of values in order
       
   987      * of types as in the "types" array from the java.io.ObjectInput
       
   988      * stream named "stream" into variables as named in "names" (for
       
   989      * any element of "names" that is null, the corresponding value is
       
   990      * unmarshalled and discarded).
       
   991      **/
       
   992     private static boolean writeUnmarshalArguments(IndentingWriter p,
       
   993                                                    String streamName,
       
   994                                                    Type[] types,
       
   995                                                    String[] names)
       
   996         throws IOException
       
   997     {
       
   998         assert types.length == names.length;
       
   999 
       
  1000         boolean readObject = false;
       
  1001         for (int i = 0; i < types.length; i++) {
       
  1002             if (writeUnmarshalArgument(p, streamName, types[i], names[i])) {
       
  1003                 readObject = true;
       
  1004             }
       
  1005             p.pln(";");
       
  1006         }
       
  1007         return readObject;
       
  1008     }
       
  1009 
       
  1010     /**
       
  1011      * Returns a snippet of Java code to wrap a value named "name" of
       
  1012      * type "type" into an object as appropriate for use by the Java
       
  1013      * Reflection API.
       
  1014      *
       
  1015      * For primitive types, an appropriate wrapper class is
       
  1016      * instantiated with the primitive value.  For object types
       
  1017      * (including arrays), no wrapping is necessary, so the value is
       
  1018      * named directly.
       
  1019      **/
       
  1020     private static String wrapArgumentCode(Type type, String name) {
       
  1021         if (type.dimension().length() > 0 || type.asClassDoc() != null) {
       
  1022             return name;
       
  1023         } else if (type.typeName().equals("boolean")) {
       
  1024             return ("(" + name +
       
  1025                     " ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)");
       
  1026         } else if (type.typeName().equals("byte")) {
       
  1027             return "new java.lang.Byte(" + name + ")";
       
  1028         } else if (type.typeName().equals("char")) {
       
  1029             return "new java.lang.Character(" + name + ")";
       
  1030         } else if (type.typeName().equals("short")) {
       
  1031             return "new java.lang.Short(" + name + ")";
       
  1032         } else if (type.typeName().equals("int")) {
       
  1033             return "new java.lang.Integer(" + name + ")";
       
  1034         } else if (type.typeName().equals("long")) {
       
  1035             return "new java.lang.Long(" + name + ")";
       
  1036         } else if (type.typeName().equals("float")) {
       
  1037             return "new java.lang.Float(" + name + ")";
       
  1038         } else if (type.typeName().equals("double")) {
       
  1039             return "new java.lang.Double(" + name + ")";
       
  1040         } else {
       
  1041             throw new AssertionError(type);
       
  1042         }
       
  1043     }
       
  1044 
       
  1045     /**
       
  1046      * Returns a snippet of Java code to unwrap a value named "name"
       
  1047      * into a value of type "type", as appropriate for the Java
       
  1048      * Reflection API.
       
  1049      *
       
  1050      * For primitive types, the value is assumed to be of the
       
  1051      * corresponding wrapper class, and a method is called on the
       
  1052      * wrapper to retrieve the primitive value.  For object types
       
  1053      * (include arrays), no unwrapping is necessary; the value is
       
  1054      * simply cast to the expected real object type.
       
  1055      **/
       
  1056     private static String unwrapArgumentCode(Type type, String name) {
       
  1057         if (type.dimension().length() > 0 || type.asClassDoc() != null) {
       
  1058             return "((" + type.toString() + ") " + name + ")";
       
  1059         } else if (type.typeName().equals("boolean")) {
       
  1060             return "((java.lang.Boolean) " + name + ").booleanValue()";
       
  1061         } else if (type.typeName().equals("byte")) {
       
  1062             return "((java.lang.Byte) " + name + ").byteValue()";
       
  1063         } else if (type.typeName().equals("char")) {
       
  1064             return "((java.lang.Character) " + name + ").charValue()";
       
  1065         } else if (type.typeName().equals("short")) {
       
  1066             return "((java.lang.Short) " + name + ").shortValue()";
       
  1067         } else if (type.typeName().equals("int")) {
       
  1068             return "((java.lang.Integer) " + name + ").intValue()";
       
  1069         } else if (type.typeName().equals("long")) {
       
  1070             return "((java.lang.Long) " + name + ").longValue()";
       
  1071         } else if (type.typeName().equals("float")) {
       
  1072             return "((java.lang.Float) " + name + ").floatValue()";
       
  1073         } else if (type.typeName().equals("double")) {
       
  1074             return "((java.lang.Double) " + name + ").doubleValue()";
       
  1075         } else {
       
  1076             throw new AssertionError(type);
       
  1077         }
       
  1078     }
       
  1079 }