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
26package sun.rmi.rmic.newrmic.jrmp;
27
28import com.sun.javadoc.ClassDoc;
29import com.sun.javadoc.MethodDoc;
30import com.sun.javadoc.Type;
31import java.io.IOException;
32import java.util.ArrayList;
33import java.util.Iterator;
34import java.util.List;
35import sun.rmi.rmic.newrmic.BatchEnvironment;
36import sun.rmi.rmic.newrmic.IndentingWriter;
37
38import static sun.rmi.rmic.newrmic.Constants.*;
39import 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 **/
51class 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}
1080