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 }