Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/rmic/RMIGenerator.java
38831 views
1
/*
2
* Copyright (c) 1997, 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
/*****************************************************************************/
27
/* Copyright (c) IBM Corporation 1998 */
28
/* */
29
/* (C) Copyright IBM Corp. 1998 */
30
/* */
31
/*****************************************************************************/
32
33
package sun.rmi.rmic;
34
35
import java.io.File;
36
import java.io.FileOutputStream;
37
import java.io.OutputStreamWriter;
38
import java.io.IOException;
39
import java.util.Enumeration;
40
import java.util.Hashtable;
41
import java.util.Vector;
42
import sun.tools.java.Type;
43
import sun.tools.java.Identifier;
44
import sun.tools.java.ClassDefinition;
45
import sun.tools.java.ClassDeclaration;
46
import sun.tools.java.ClassNotFound;
47
import sun.tools.java.ClassFile;
48
import sun.tools.java.MemberDefinition;
49
import com.sun.corba.se.impl.util.Utility;
50
51
/**
52
* A Generator object will generate the Java source code of the stub
53
* and skeleton classes for an RMI remote implementation class, using
54
* a particular stub protocol version.
55
*
56
* WARNING: The contents of this source file are not part of any
57
* supported API. Code that depends on them does so at its own risk:
58
* they are subject to change or removal without notice.
59
*
60
* @author Peter Jones, Bryan Atsatt
61
*/
62
public class RMIGenerator implements RMIConstants, Generator {
63
64
private static final Hashtable<String, Integer> versionOptions = new Hashtable<>();
65
static {
66
versionOptions.put("-v1.1", new Integer(STUB_VERSION_1_1));
67
versionOptions.put("-vcompat", new Integer(STUB_VERSION_FAT));
68
versionOptions.put("-v1.2", new Integer(STUB_VERSION_1_2));
69
}
70
71
/**
72
* Default constructor for Main to use.
73
*/
74
public RMIGenerator() {
75
version = STUB_VERSION_1_2; // default is -v1.2 (see 4638155)
76
}
77
78
/**
79
* Examine and consume command line arguments.
80
* @param argv The command line arguments. Ignore null
81
* and unknown arguments. Set each consumed argument to null.
82
* @param error Report any errors using the main.error() methods.
83
* @return true if no errors, false otherwise.
84
*/
85
public boolean parseArgs(String argv[], Main main) {
86
String explicitVersion = null;
87
for (int i = 0; i < argv.length; i++) {
88
if (argv[i] != null) {
89
String arg = argv[i].toLowerCase();
90
if (versionOptions.containsKey(arg)) {
91
if (explicitVersion != null &&
92
!explicitVersion.equals(arg))
93
{
94
main.error("rmic.cannot.use.both",
95
explicitVersion, arg);
96
return false;
97
}
98
explicitVersion = arg;
99
version = versionOptions.get(arg);
100
argv[i] = null;
101
}
102
}
103
}
104
return true;
105
}
106
107
/**
108
* Generate the source files for the stub and/or skeleton classes
109
* needed by RMI for the given remote implementation class.
110
*
111
* @param env compiler environment
112
* @param cdef definition of remote implementation class
113
* to generate stubs and/or skeletons for
114
* @param destDir directory for the root of the package hierarchy
115
* for generated files
116
*/
117
public void generate(BatchEnvironment env, ClassDefinition cdef, File destDir) {
118
RemoteClass remoteClass = RemoteClass.forClass(env, cdef);
119
if (remoteClass == null) // exit if an error occurred
120
return;
121
122
RMIGenerator gen;
123
try {
124
gen = new RMIGenerator(env, cdef, destDir, remoteClass, version);
125
} catch (ClassNotFound e) {
126
env.error(0, "rmic.class.not.found", e.name);
127
return;
128
}
129
gen.generate();
130
}
131
132
private void generate() {
133
env.addGeneratedFile(stubFile);
134
135
try {
136
IndentingWriter out = new IndentingWriter(
137
new OutputStreamWriter(new FileOutputStream(stubFile)));
138
writeStub(out);
139
out.close();
140
if (env.verbose()) {
141
env.output(Main.getText("rmic.wrote", stubFile.getPath()));
142
}
143
env.parseFile(new ClassFile(stubFile));
144
} catch (IOException e) {
145
env.error(0, "cant.write", stubFile.toString());
146
return;
147
}
148
149
if (version == STUB_VERSION_1_1 ||
150
version == STUB_VERSION_FAT)
151
{
152
env.addGeneratedFile(skeletonFile);
153
154
try {
155
IndentingWriter out = new IndentingWriter(
156
new OutputStreamWriter(
157
new FileOutputStream(skeletonFile)));
158
writeSkeleton(out);
159
out.close();
160
if (env.verbose()) {
161
env.output(Main.getText("rmic.wrote",
162
skeletonFile.getPath()));
163
}
164
env.parseFile(new ClassFile(skeletonFile));
165
} catch (IOException e) {
166
env.error(0, "cant.write", stubFile.toString());
167
return;
168
}
169
} else {
170
/*
171
* For bugid 4135136: if skeleton files are not being generated
172
* for this compilation run, delete old skeleton source or class
173
* files for this remote implementation class that were
174
* (presumably) left over from previous runs, to avoid user
175
* confusion from extraneous or inconsistent generated files.
176
*/
177
178
File outputDir = Util.getOutputDirectoryFor(remoteClassName,destDir,env);
179
File skeletonClassFile = new File(outputDir,skeletonClassName.getName().toString() + ".class");
180
181
skeletonFile.delete(); // ignore failures (no big deal)
182
skeletonClassFile.delete();
183
}
184
}
185
186
/**
187
* Return the File object that should be used as the source file
188
* for the given Java class, using the supplied destination
189
* directory for the top of the package hierarchy.
190
*/
191
protected static File sourceFileForClass(Identifier className,
192
Identifier outputClassName,
193
File destDir,
194
BatchEnvironment env)
195
{
196
File packageDir = Util.getOutputDirectoryFor(className,destDir,env);
197
String outputName = Names.mangleClass(outputClassName).getName().toString();
198
199
// Is there any existing _Tie equivalent leftover from a
200
// previous invocation of rmic -iiop? Only do this once per
201
// class by looking for skeleton generation...
202
203
if (outputName.endsWith("_Skel")) {
204
String classNameStr = className.getName().toString();
205
File temp = new File(packageDir, Utility.tieName(classNameStr) + ".class");
206
if (temp.exists()) {
207
208
// Found a tie. Is IIOP generation also being done?
209
210
if (!env.getMain().iiopGeneration) {
211
212
// No, so write a warning...
213
214
env.error(0,"warn.rmic.tie.found",
215
classNameStr,
216
temp.getAbsolutePath());
217
}
218
}
219
}
220
221
String outputFileName = outputName + ".java";
222
return new File(packageDir, outputFileName);
223
}
224
225
226
/** rmic environment for this object */
227
private BatchEnvironment env;
228
229
/** the remote class that this instance is generating code for */
230
private RemoteClass remoteClass;
231
232
/** version of the stub protocol to use in code generation */
233
private int version;
234
235
/** remote methods for remote class, indexed by operation number */
236
private RemoteClass.Method[] remoteMethods;
237
238
/**
239
* Names for the remote class and the stub and skeleton classes
240
* to be generated for it.
241
*/
242
private Identifier remoteClassName;
243
private Identifier stubClassName;
244
private Identifier skeletonClassName;
245
246
private ClassDefinition cdef;
247
private File destDir;
248
private File stubFile;
249
private File skeletonFile;
250
251
/**
252
* Names to use for the java.lang.reflect.Method static fields
253
* corresponding to each remote method.
254
*/
255
private String[] methodFieldNames;
256
257
/** cached definition for certain exception classes in this environment */
258
private ClassDefinition defException;
259
private ClassDefinition defRemoteException;
260
private ClassDefinition defRuntimeException;
261
262
/**
263
* Create a new stub/skeleton Generator object for the given
264
* remote implementation class to generate code according to
265
* the given stub protocol version.
266
*/
267
private RMIGenerator(BatchEnvironment env, ClassDefinition cdef,
268
File destDir, RemoteClass remoteClass, int version)
269
throws ClassNotFound
270
{
271
this.destDir = destDir;
272
this.cdef = cdef;
273
this.env = env;
274
this.remoteClass = remoteClass;
275
this.version = version;
276
277
remoteMethods = remoteClass.getRemoteMethods();
278
279
remoteClassName = remoteClass.getName();
280
stubClassName = Names.stubFor(remoteClassName);
281
skeletonClassName = Names.skeletonFor(remoteClassName);
282
283
methodFieldNames = nameMethodFields(remoteMethods);
284
285
stubFile = sourceFileForClass(remoteClassName,stubClassName, destDir , env);
286
skeletonFile = sourceFileForClass(remoteClassName,skeletonClassName, destDir, env);
287
288
/*
289
* Initialize cached definitions for exception classes used
290
* in the generation process.
291
*/
292
defException =
293
env.getClassDeclaration(idJavaLangException).
294
getClassDefinition(env);
295
defRemoteException =
296
env.getClassDeclaration(idRemoteException).
297
getClassDefinition(env);
298
defRuntimeException =
299
env.getClassDeclaration(idJavaLangRuntimeException).
300
getClassDefinition(env);
301
}
302
303
/**
304
* Write the stub for the remote class to a stream.
305
*/
306
private void writeStub(IndentingWriter p) throws IOException {
307
308
/*
309
* Write boiler plate comment.
310
*/
311
p.pln("// Stub class generated by rmic, do not edit.");
312
p.pln("// Contents subject to change without notice.");
313
p.pln();
314
315
/*
316
* If remote implementation class was in a particular package,
317
* declare the stub class to be in the same package.
318
*/
319
if (remoteClassName.isQualified()) {
320
p.pln("package " + remoteClassName.getQualifier() + ";");
321
p.pln();
322
}
323
324
/*
325
* Declare the stub class; implement all remote interfaces.
326
*/
327
p.plnI("public final class " +
328
Names.mangleClass(stubClassName.getName()));
329
p.pln("extends " + idRemoteStub);
330
ClassDefinition[] remoteInterfaces = remoteClass.getRemoteInterfaces();
331
if (remoteInterfaces.length > 0) {
332
p.p("implements ");
333
for (int i = 0; i < remoteInterfaces.length; i++) {
334
if (i > 0)
335
p.p(", ");
336
p.p(remoteInterfaces[i].getName().toString());
337
}
338
p.pln();
339
}
340
p.pOlnI("{");
341
342
if (version == STUB_VERSION_1_1 ||
343
version == STUB_VERSION_FAT)
344
{
345
writeOperationsArray(p);
346
p.pln();
347
writeInterfaceHash(p);
348
p.pln();
349
}
350
351
if (version == STUB_VERSION_FAT ||
352
version == STUB_VERSION_1_2)
353
{
354
p.pln("private static final long serialVersionUID = " +
355
STUB_SERIAL_VERSION_UID + ";");
356
p.pln();
357
358
/*
359
* We only need to declare and initialize the static fields of
360
* Method objects for each remote method if there are any remote
361
* methods; otherwise, skip this code entirely, to avoid generating
362
* a try/catch block for a checked exception that cannot occur
363
* (see bugid 4125181).
364
*/
365
if (methodFieldNames.length > 0) {
366
if (version == STUB_VERSION_FAT) {
367
p.pln("private static boolean useNewInvoke;");
368
}
369
writeMethodFieldDeclarations(p);
370
p.pln();
371
372
/*
373
* Initialize java.lang.reflect.Method fields for each remote
374
* method in a static initializer.
375
*/
376
p.plnI("static {");
377
p.plnI("try {");
378
if (version == STUB_VERSION_FAT) {
379
/*
380
* Fat stubs must determine whether the API required for
381
* the JDK 1.2 stub protocol is supported in the current
382
* runtime, so that it can use it if supported. This is
383
* determined by using the Reflection API to test if the
384
* new invoke method on RemoteRef exists, and setting the
385
* static boolean "useNewInvoke" to true if it does, or
386
* to false if a NoSuchMethodException is thrown.
387
*/
388
p.plnI(idRemoteRef + ".class.getMethod(\"invoke\",");
389
p.plnI("new java.lang.Class[] {");
390
p.pln(idRemote + ".class,");
391
p.pln("java.lang.reflect.Method.class,");
392
p.pln("java.lang.Object[].class,");
393
p.pln("long.class");
394
p.pOln("});");
395
p.pO();
396
p.pln("useNewInvoke = true;");
397
}
398
writeMethodFieldInitializers(p);
399
p.pOlnI("} catch (java.lang.NoSuchMethodException e) {");
400
if (version == STUB_VERSION_FAT) {
401
p.pln("useNewInvoke = false;");
402
} else {
403
/*
404
* REMIND: By throwing an Error here, the application will
405
* get the NoSuchMethodError directly when the stub class
406
* is initialized. If we throw a RuntimeException
407
* instead, the application would get an
408
* ExceptionInInitializerError. Would that be more
409
* appropriate, and if so, which RuntimeException should
410
* be thrown?
411
*/
412
p.plnI("throw new java.lang.NoSuchMethodError(");
413
p.pln("\"stub class initialization failed\");");
414
p.pO();
415
}
416
p.pOln("}"); // end try/catch block
417
p.pOln("}"); // end static initializer
418
p.pln();
419
}
420
}
421
422
writeStubConstructors(p);
423
p.pln();
424
425
/*
426
* Write each stub method.
427
*/
428
if (remoteMethods.length > 0) {
429
p.pln("// methods from remote interfaces");
430
for (int i = 0; i < remoteMethods.length; ++i) {
431
p.pln();
432
writeStubMethod(p, i);
433
}
434
}
435
436
p.pOln("}"); // end stub class
437
}
438
439
/**
440
* Write the constructors for the stub class.
441
*/
442
private void writeStubConstructors(IndentingWriter p)
443
throws IOException
444
{
445
p.pln("// constructors");
446
447
/*
448
* Only stubs compatible with the JDK 1.1 stub protocol need
449
* a no-arg constructor; later versions use reflection to find
450
* the constructor that directly takes a RemoteRef argument.
451
*/
452
if (version == STUB_VERSION_1_1 ||
453
version == STUB_VERSION_FAT)
454
{
455
p.plnI("public " + Names.mangleClass(stubClassName.getName()) +
456
"() {");
457
p.pln("super();");
458
p.pOln("}");
459
}
460
461
p.plnI("public " + Names.mangleClass(stubClassName.getName()) +
462
"(" + idRemoteRef + " ref) {");
463
p.pln("super(ref);");
464
p.pOln("}");
465
}
466
467
/**
468
* Write the stub method for the remote method with the given "opnum".
469
*/
470
private void writeStubMethod(IndentingWriter p, int opnum)
471
throws IOException
472
{
473
RemoteClass.Method method = remoteMethods[opnum];
474
Identifier methodName = method.getName();
475
Type methodType = method.getType();
476
Type paramTypes[] = methodType.getArgumentTypes();
477
String paramNames[] = nameParameters(paramTypes);
478
Type returnType = methodType.getReturnType();
479
ClassDeclaration[] exceptions = method.getExceptions();
480
481
/*
482
* Declare stub method; throw exceptions declared in remote
483
* interface(s).
484
*/
485
p.pln("// implementation of " +
486
methodType.typeString(methodName.toString(), true, false));
487
p.p("public " + returnType + " " + methodName + "(");
488
for (int i = 0; i < paramTypes.length; i++) {
489
if (i > 0)
490
p.p(", ");
491
p.p(paramTypes[i] + " " + paramNames[i]);
492
}
493
p.plnI(")");
494
if (exceptions.length > 0) {
495
p.p("throws ");
496
for (int i = 0; i < exceptions.length; i++) {
497
if (i > 0)
498
p.p(", ");
499
p.p(exceptions[i].getName().toString());
500
}
501
p.pln();
502
}
503
p.pOlnI("{");
504
505
/*
506
* The RemoteRef.invoke methods throw Exception, but unless this
507
* stub method throws Exception as well, we must catch Exceptions
508
* thrown from the invocation. So we must catch Exception and
509
* rethrow something we can throw: UnexpectedException, which is a
510
* subclass of RemoteException. But for any subclasses of Exception
511
* that we can throw, like RemoteException, RuntimeException, and
512
* any of the exceptions declared by this stub method, we want them
513
* to pass through unharmed, so first we must catch any such
514
* exceptions and rethrow it directly.
515
*
516
* We have to be careful generating the rethrowing catch blocks
517
* here, because javac will flag an error if there are any
518
* unreachable catch blocks, i.e. if the catch of an exception class
519
* follows a previous catch of it or of one of its superclasses.
520
* The following method invocation takes care of these details.
521
*/
522
Vector<ClassDefinition> catchList = computeUniqueCatchList(exceptions);
523
524
/*
525
* If we need to catch any particular exceptions (i.e. this method
526
* does not declare java.lang.Exception), put the entire stub
527
* method in a try block.
528
*/
529
if (catchList.size() > 0) {
530
p.plnI("try {");
531
}
532
533
if (version == STUB_VERSION_FAT) {
534
p.plnI("if (useNewInvoke) {");
535
}
536
if (version == STUB_VERSION_FAT ||
537
version == STUB_VERSION_1_2)
538
{
539
if (!returnType.isType(TC_VOID)) {
540
p.p("Object $result = "); // REMIND: why $?
541
}
542
p.p("ref.invoke(this, " + methodFieldNames[opnum] + ", ");
543
if (paramTypes.length > 0) {
544
p.p("new java.lang.Object[] {");
545
for (int i = 0; i < paramTypes.length; i++) {
546
if (i > 0)
547
p.p(", ");
548
p.p(wrapArgumentCode(paramTypes[i], paramNames[i]));
549
}
550
p.p("}");
551
} else {
552
p.p("null");
553
}
554
p.pln(", " + method.getMethodHash() + "L);");
555
if (!returnType.isType(TC_VOID)) {
556
p.pln("return " +
557
unwrapArgumentCode(returnType, "$result") + ";");
558
}
559
}
560
if (version == STUB_VERSION_FAT) {
561
p.pOlnI("} else {");
562
}
563
if (version == STUB_VERSION_1_1 ||
564
version == STUB_VERSION_FAT)
565
{
566
p.pln(idRemoteCall + " call = ref.newCall((" + idRemoteObject +
567
") this, operations, " + opnum + ", interfaceHash);");
568
569
if (paramTypes.length > 0) {
570
p.plnI("try {");
571
p.pln("java.io.ObjectOutput out = call.getOutputStream();");
572
writeMarshalArguments(p, "out", paramTypes, paramNames);
573
p.pOlnI("} catch (java.io.IOException e) {");
574
p.pln("throw new " + idMarshalException +
575
"(\"error marshalling arguments\", e);");
576
p.pOln("}");
577
}
578
579
p.pln("ref.invoke(call);");
580
581
if (returnType.isType(TC_VOID)) {
582
p.pln("ref.done(call);");
583
} else {
584
p.pln(returnType + " $result;"); // REMIND: why $?
585
p.plnI("try {");
586
p.pln("java.io.ObjectInput in = call.getInputStream();");
587
boolean objectRead =
588
writeUnmarshalArgument(p, "in", returnType, "$result");
589
p.pln(";");
590
p.pOlnI("} catch (java.io.IOException e) {");
591
p.pln("throw new " + idUnmarshalException +
592
"(\"error unmarshalling return\", e);");
593
/*
594
* If any only if readObject has been invoked, we must catch
595
* ClassNotFoundException as well as IOException.
596
*/
597
if (objectRead) {
598
p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");
599
p.pln("throw new " + idUnmarshalException +
600
"(\"error unmarshalling return\", e);");
601
}
602
p.pOlnI("} finally {");
603
p.pln("ref.done(call);");
604
p.pOln("}");
605
p.pln("return $result;");
606
}
607
}
608
if (version == STUB_VERSION_FAT) {
609
p.pOln("}"); // end if/else (useNewInvoke) block
610
}
611
612
/*
613
* If we need to catch any particular exceptions, finally write
614
* the catch blocks for them, rethrow any other Exceptions with an
615
* UnexpectedException, and end the try block.
616
*/
617
if (catchList.size() > 0) {
618
for (Enumeration<ClassDefinition> enumeration = catchList.elements();
619
enumeration.hasMoreElements();)
620
{
621
ClassDefinition def = enumeration.nextElement();
622
p.pOlnI("} catch (" + def.getName() + " e) {");
623
p.pln("throw e;");
624
}
625
p.pOlnI("} catch (java.lang.Exception e) {");
626
p.pln("throw new " + idUnexpectedException +
627
"(\"undeclared checked exception\", e);");
628
p.pOln("}"); // end try/catch block
629
}
630
631
p.pOln("}"); // end stub method
632
}
633
634
/**
635
* Compute the exceptions which need to be caught and rethrown in a
636
* stub method before wrapping Exceptions in UnexpectedExceptions,
637
* given the exceptions declared in the throws clause of the method.
638
* Returns a Vector containing ClassDefinition objects for each
639
* exception to catch. Each exception is guaranteed to be unique,
640
* i.e. not a subclass of any of the other exceptions in the Vector,
641
* so the catch blocks for these exceptions may be generated in any
642
* order relative to each other.
643
*
644
* RemoteException and RuntimeException are each automatically placed
645
* in the returned Vector (if none of their superclasses are already
646
* present), since those exceptions should always be directly rethrown
647
* by a stub method.
648
*
649
* The returned Vector will be empty if java.lang.Exception or one
650
* of its superclasses is in the throws clause of the method, indicating
651
* that no exceptions need to be caught.
652
*/
653
private Vector<ClassDefinition> computeUniqueCatchList(ClassDeclaration[] exceptions) {
654
Vector<ClassDefinition> uniqueList = new Vector<>(); // unique exceptions to catch
655
656
uniqueList.addElement(defRuntimeException);
657
uniqueList.addElement(defRemoteException);
658
659
/* For each exception declared by the stub method's throws clause: */
660
nextException:
661
for (int i = 0; i < exceptions.length; i++) {
662
ClassDeclaration decl = exceptions[i];
663
try {
664
if (defException.subClassOf(env, decl)) {
665
/*
666
* (If java.lang.Exception (or a superclass) was declared
667
* in the throws clause of this stub method, then we don't
668
* have to bother catching anything; clear the list and
669
* return.)
670
*/
671
uniqueList.clear();
672
break;
673
} else if (!defException.superClassOf(env, decl)) {
674
/*
675
* Ignore other Throwables that do not extend Exception,
676
* since they do not need to be caught anyway.
677
*/
678
continue;
679
}
680
/*
681
* Compare this exception against the current list of
682
* exceptions that need to be caught:
683
*/
684
for (int j = 0; j < uniqueList.size();) {
685
ClassDefinition def = uniqueList.elementAt(j);
686
if (def.superClassOf(env, decl)) {
687
/*
688
* If a superclass of this exception is already on
689
* the list to catch, then ignore and continue;
690
*/
691
continue nextException;
692
} else if (def.subClassOf(env, decl)) {
693
/*
694
* If a subclass of this exception is on the list
695
* to catch, then remove it.
696
*/
697
uniqueList.removeElementAt(j);
698
} else {
699
j++; // else continue comparing
700
}
701
}
702
/* This exception is unique: add it to the list to catch. */
703
uniqueList.addElement(decl.getClassDefinition(env));
704
} catch (ClassNotFound e) {
705
env.error(0, "class.not.found", e.name, decl.getName());
706
/*
707
* REMIND: We do not exit from this exceptional condition,
708
* generating questionable code and likely letting the
709
* compiler report a resulting error later.
710
*/
711
}
712
}
713
return uniqueList;
714
}
715
716
/**
717
* Write the skeleton for the remote class to a stream.
718
*/
719
private void writeSkeleton(IndentingWriter p) throws IOException {
720
if (version == STUB_VERSION_1_2) {
721
throw new Error("should not generate skeleton for version");
722
}
723
724
/*
725
* Write boiler plate comment.
726
*/
727
p.pln("// Skeleton class generated by rmic, do not edit.");
728
p.pln("// Contents subject to change without notice.");
729
p.pln();
730
731
/*
732
* If remote implementation class was in a particular package,
733
* declare the skeleton class to be in the same package.
734
*/
735
if (remoteClassName.isQualified()) {
736
p.pln("package " + remoteClassName.getQualifier() + ";");
737
p.pln();
738
}
739
740
/*
741
* Declare the skeleton class.
742
*/
743
p.plnI("public final class " +
744
Names.mangleClass(skeletonClassName.getName()));
745
p.pln("implements " + idSkeleton);
746
p.pOlnI("{");
747
748
writeOperationsArray(p);
749
p.pln();
750
751
writeInterfaceHash(p);
752
p.pln();
753
754
/*
755
* Define the getOperations() method.
756
*/
757
p.plnI("public " + idOperation + "[] getOperations() {");
758
p.pln("return (" + idOperation + "[]) operations.clone();");
759
p.pOln("}");
760
p.pln();
761
762
/*
763
* Define the dispatch() method.
764
*/
765
p.plnI("public void dispatch(" + idRemote + " obj, " +
766
idRemoteCall + " call, int opnum, long hash)");
767
p.pln("throws java.lang.Exception");
768
p.pOlnI("{");
769
770
if (version == STUB_VERSION_FAT) {
771
p.plnI("if (opnum < 0) {");
772
if (remoteMethods.length > 0) {
773
for (int opnum = 0; opnum < remoteMethods.length; opnum++) {
774
if (opnum > 0)
775
p.pO("} else ");
776
p.plnI("if (hash == " +
777
remoteMethods[opnum].getMethodHash() + "L) {");
778
p.pln("opnum = " + opnum + ";");
779
}
780
p.pOlnI("} else {");
781
}
782
/*
783
* Skeleton throws UnmarshalException if it does not recognize
784
* the method hash; this is what UnicastServerRef.dispatch()
785
* would do.
786
*/
787
p.pln("throw new " +
788
idUnmarshalException + "(\"invalid method hash\");");
789
if (remoteMethods.length > 0) {
790
p.pOln("}");
791
}
792
/*
793
* Ignore the validation of the interface hash if the
794
* operation number was negative, since it is really a
795
* method hash instead.
796
*/
797
p.pOlnI("} else {");
798
}
799
800
p.plnI("if (hash != interfaceHash)");
801
p.pln("throw new " +
802
idSkeletonMismatchException + "(\"interface hash mismatch\");");
803
p.pO();
804
805
if (version == STUB_VERSION_FAT) {
806
p.pOln("}"); // end if/else (opnum < 0) block
807
}
808
p.pln();
809
810
/*
811
* Cast remote object instance to our specific implementation class.
812
*/
813
p.pln(remoteClassName + " server = (" + remoteClassName + ") obj;");
814
815
/*
816
* Process call according to the operation number.
817
*/
818
p.plnI("switch (opnum) {");
819
for (int opnum = 0; opnum < remoteMethods.length; opnum++) {
820
writeSkeletonDispatchCase(p, opnum);
821
}
822
p.pOlnI("default:");
823
/*
824
* Skeleton throws UnmarshalException if it does not recognize
825
* the operation number; this is consistent with the case of an
826
* unrecognized method hash.
827
*/
828
p.pln("throw new " + idUnmarshalException +
829
"(\"invalid method number\");");
830
p.pOln("}"); // end switch statement
831
832
p.pOln("}"); // end dispatch() method
833
834
p.pOln("}"); // end skeleton class
835
}
836
837
/**
838
* Write the case block for the skeleton's dispatch method for
839
* the remote method with the given "opnum".
840
*/
841
private void writeSkeletonDispatchCase(IndentingWriter p, int opnum)
842
throws IOException
843
{
844
RemoteClass.Method method = remoteMethods[opnum];
845
Identifier methodName = method.getName();
846
Type methodType = method.getType();
847
Type paramTypes[] = methodType.getArgumentTypes();
848
String paramNames[] = nameParameters(paramTypes);
849
Type returnType = methodType.getReturnType();
850
851
p.pOlnI("case " + opnum + ": // " +
852
methodType.typeString(methodName.toString(), true, false));
853
/*
854
* Use nested block statement inside case to provide an independent
855
* namespace for local variables used to unmarshal parameters for
856
* this remote method.
857
*/
858
p.pOlnI("{");
859
860
if (paramTypes.length > 0) {
861
/*
862
* Declare local variables to hold arguments.
863
*/
864
for (int i = 0; i < paramTypes.length; i++) {
865
p.pln(paramTypes[i] + " " + paramNames[i] + ";");
866
}
867
868
/*
869
* Unmarshal arguments from call stream.
870
*/
871
p.plnI("try {");
872
p.pln("java.io.ObjectInput in = call.getInputStream();");
873
boolean objectsRead = writeUnmarshalArguments(p, "in",
874
paramTypes, paramNames);
875
p.pOlnI("} catch (java.io.IOException e) {");
876
p.pln("throw new " + idUnmarshalException +
877
"(\"error unmarshalling arguments\", e);");
878
/*
879
* If any only if readObject has been invoked, we must catch
880
* ClassNotFoundException as well as IOException.
881
*/
882
if (objectsRead) {
883
p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");
884
p.pln("throw new " + idUnmarshalException +
885
"(\"error unmarshalling arguments\", e);");
886
}
887
p.pOlnI("} finally {");
888
p.pln("call.releaseInputStream();");
889
p.pOln("}");
890
} else {
891
p.pln("call.releaseInputStream();");
892
}
893
894
if (!returnType.isType(TC_VOID)) {
895
/*
896
* Declare variable to hold return type, if not void.
897
*/
898
p.p(returnType + " $result = "); // REMIND: why $?
899
}
900
901
/*
902
* Invoke the method on the server object.
903
*/
904
p.p("server." + methodName + "(");
905
for (int i = 0; i < paramNames.length; i++) {
906
if (i > 0)
907
p.p(", ");
908
p.p(paramNames[i]);
909
}
910
p.pln(");");
911
912
/*
913
* Always invoke getResultStream(true) on the call object to send
914
* the indication of a successful invocation to the caller. If
915
* the return type is not void, keep the result stream and marshal
916
* the return value.
917
*/
918
p.plnI("try {");
919
if (!returnType.isType(TC_VOID)) {
920
p.p("java.io.ObjectOutput out = ");
921
}
922
p.pln("call.getResultStream(true);");
923
if (!returnType.isType(TC_VOID)) {
924
writeMarshalArgument(p, "out", returnType, "$result");
925
p.pln(";");
926
}
927
p.pOlnI("} catch (java.io.IOException e) {");
928
p.pln("throw new " +
929
idMarshalException + "(\"error marshalling return\", e);");
930
p.pOln("}");
931
932
p.pln("break;"); // break from switch statement
933
934
p.pOlnI("}"); // end nested block statement
935
p.pln();
936
}
937
938
/**
939
* Write declaration and initializer for "operations" static array.
940
*/
941
private void writeOperationsArray(IndentingWriter p)
942
throws IOException
943
{
944
p.plnI("private static final " + idOperation + "[] operations = {");
945
for (int i = 0; i < remoteMethods.length; i++) {
946
if (i > 0)
947
p.pln(",");
948
p.p("new " + idOperation + "(\"" +
949
remoteMethods[i].getOperationString() + "\")");
950
}
951
p.pln();
952
p.pOln("};");
953
}
954
955
/**
956
* Write declaration and initializer for "interfaceHash" static field.
957
*/
958
private void writeInterfaceHash(IndentingWriter p)
959
throws IOException
960
{
961
p.pln("private static final long interfaceHash = " +
962
remoteClass.getInterfaceHash() + "L;");
963
}
964
965
/**
966
* Write declaration for java.lang.reflect.Method static fields
967
* corresponding to each remote method in a stub.
968
*/
969
private void writeMethodFieldDeclarations(IndentingWriter p)
970
throws IOException
971
{
972
for (int i = 0; i < methodFieldNames.length; i++) {
973
p.pln("private static java.lang.reflect.Method " +
974
methodFieldNames[i] + ";");
975
}
976
}
977
978
/**
979
* Write code to initialize the static fields for each method
980
* using the Java Reflection API.
981
*/
982
private void writeMethodFieldInitializers(IndentingWriter p)
983
throws IOException
984
{
985
for (int i = 0; i < methodFieldNames.length; i++) {
986
p.p(methodFieldNames[i] + " = ");
987
/*
988
* Here we look up the Method object in the arbitrary interface
989
* that we find in the RemoteClass.Method object.
990
* REMIND: Is this arbitrary choice OK?
991
* REMIND: Should this access be part of RemoteClass.Method's
992
* abstraction?
993
*/
994
RemoteClass.Method method = remoteMethods[i];
995
MemberDefinition def = method.getMemberDefinition();
996
Identifier methodName = method.getName();
997
Type methodType = method.getType();
998
Type paramTypes[] = methodType.getArgumentTypes();
999
1000
p.p(def.getClassDefinition().getName() + ".class.getMethod(\"" +
1001
methodName + "\", new java.lang.Class[] {");
1002
for (int j = 0; j < paramTypes.length; j++) {
1003
if (j > 0)
1004
p.p(", ");
1005
p.p(paramTypes[j] + ".class");
1006
}
1007
p.pln("});");
1008
}
1009
}
1010
1011
1012
/*
1013
* Following are a series of static utility methods useful during
1014
* the code generation process:
1015
*/
1016
1017
/**
1018
* Generate an array of names for fields that correspond to the given
1019
* array of remote methods. Each name in the returned array is
1020
* guaranteed to be unique.
1021
*
1022
* The name of a method is included in its corresponding field name
1023
* to enhance readability of the generated code.
1024
*/
1025
private static String[] nameMethodFields(RemoteClass.Method[] methods) {
1026
String[] names = new String[methods.length];
1027
for (int i = 0; i < names.length; i++) {
1028
names[i] = "$method_" + methods[i].getName() + "_" + i;
1029
}
1030
return names;
1031
}
1032
1033
/**
1034
* Generate an array of names for parameters corresponding to the
1035
* given array of types for the parameters. Each name in the returned
1036
* array is guaranteed to be unique.
1037
*
1038
* A representation of the type of a parameter is included in its
1039
* corresponding field name to enhance the readability of the generated
1040
* code.
1041
*/
1042
private static String[] nameParameters(Type[] types) {
1043
String[] names = new String[types.length];
1044
for (int i = 0; i < names.length; i++) {
1045
names[i] = "$param_" +
1046
generateNameFromType(types[i]) + "_" + (i + 1);
1047
}
1048
return names;
1049
}
1050
1051
/**
1052
* Generate a readable string representing the given type suitable
1053
* for embedding within a Java identifier.
1054
*/
1055
private static String generateNameFromType(Type type) {
1056
int typeCode = type.getTypeCode();
1057
switch (typeCode) {
1058
case TC_BOOLEAN:
1059
case TC_BYTE:
1060
case TC_CHAR:
1061
case TC_SHORT:
1062
case TC_INT:
1063
case TC_LONG:
1064
case TC_FLOAT:
1065
case TC_DOUBLE:
1066
return type.toString();
1067
case TC_ARRAY:
1068
return "arrayOf_" + generateNameFromType(type.getElementType());
1069
case TC_CLASS:
1070
return Names.mangleClass(type.getClassName().getName()).toString();
1071
default:
1072
throw new Error("unexpected type code: " + typeCode);
1073
}
1074
}
1075
1076
/**
1077
* Write a snippet of Java code to marshal a value named "name" of
1078
* type "type" to the java.io.ObjectOutput stream named "stream".
1079
*
1080
* Primitive types are marshalled with their corresponding methods
1081
* in the java.io.DataOutput interface, and objects (including arrays)
1082
* are marshalled using the writeObject method.
1083
*/
1084
private static void writeMarshalArgument(IndentingWriter p,
1085
String streamName,
1086
Type type, String name)
1087
throws IOException
1088
{
1089
int typeCode = type.getTypeCode();
1090
switch (typeCode) {
1091
case TC_BOOLEAN:
1092
p.p(streamName + ".writeBoolean(" + name + ")");
1093
break;
1094
case TC_BYTE:
1095
p.p(streamName + ".writeByte(" + name + ")");
1096
break;
1097
case TC_CHAR:
1098
p.p(streamName + ".writeChar(" + name + ")");
1099
break;
1100
case TC_SHORT:
1101
p.p(streamName + ".writeShort(" + name + ")");
1102
break;
1103
case TC_INT:
1104
p.p(streamName + ".writeInt(" + name + ")");
1105
break;
1106
case TC_LONG:
1107
p.p(streamName + ".writeLong(" + name + ")");
1108
break;
1109
case TC_FLOAT:
1110
p.p(streamName + ".writeFloat(" + name + ")");
1111
break;
1112
case TC_DOUBLE:
1113
p.p(streamName + ".writeDouble(" + name + ")");
1114
break;
1115
case TC_ARRAY:
1116
case TC_CLASS:
1117
p.p(streamName + ".writeObject(" + name + ")");
1118
break;
1119
default:
1120
throw new Error("unexpected type code: " + typeCode);
1121
}
1122
}
1123
1124
/**
1125
* Write Java statements to marshal a series of values in order as
1126
* named in the "names" array, with types as specified in the "types"
1127
* array", to the java.io.ObjectOutput stream named "stream".
1128
*/
1129
private static void writeMarshalArguments(IndentingWriter p,
1130
String streamName,
1131
Type[] types, String[] names)
1132
throws IOException
1133
{
1134
if (types.length != names.length) {
1135
throw new Error("parameter type and name arrays different sizes");
1136
}
1137
1138
for (int i = 0; i < types.length; i++) {
1139
writeMarshalArgument(p, streamName, types[i], names[i]);
1140
p.pln(";");
1141
}
1142
}
1143
1144
/**
1145
* Write a snippet of Java code to unmarshal a value of type "type"
1146
* from the java.io.ObjectInput stream named "stream" into a variable
1147
* named "name" (if "name" is null, the value in unmarshalled and
1148
* discarded).
1149
*
1150
* Primitive types are unmarshalled with their corresponding methods
1151
* in the java.io.DataInput interface, and objects (including arrays)
1152
* are unmarshalled using the readObject method.
1153
*/
1154
private static boolean writeUnmarshalArgument(IndentingWriter p,
1155
String streamName,
1156
Type type, String name)
1157
throws IOException
1158
{
1159
boolean readObject = false;
1160
1161
if (name != null) {
1162
p.p(name + " = ");
1163
}
1164
1165
int typeCode = type.getTypeCode();
1166
switch (type.getTypeCode()) {
1167
case TC_BOOLEAN:
1168
p.p(streamName + ".readBoolean()");
1169
break;
1170
case TC_BYTE:
1171
p.p(streamName + ".readByte()");
1172
break;
1173
case TC_CHAR:
1174
p.p(streamName + ".readChar()");
1175
break;
1176
case TC_SHORT:
1177
p.p(streamName + ".readShort()");
1178
break;
1179
case TC_INT:
1180
p.p(streamName + ".readInt()");
1181
break;
1182
case TC_LONG:
1183
p.p(streamName + ".readLong()");
1184
break;
1185
case TC_FLOAT:
1186
p.p(streamName + ".readFloat()");
1187
break;
1188
case TC_DOUBLE:
1189
p.p(streamName + ".readDouble()");
1190
break;
1191
case TC_ARRAY:
1192
case TC_CLASS:
1193
p.p("(" + type + ") " + streamName + ".readObject()");
1194
readObject = true;
1195
break;
1196
default:
1197
throw new Error("unexpected type code: " + typeCode);
1198
}
1199
return readObject;
1200
}
1201
1202
/**
1203
* Write Java statements to unmarshal a series of values in order of
1204
* types as in the "types" array from the java.io.ObjectInput stream
1205
* named "stream" into variables as named in "names" (for any element
1206
* of "names" that is null, the corresponding value is unmarshalled
1207
* and discarded).
1208
*/
1209
private static boolean writeUnmarshalArguments(IndentingWriter p,
1210
String streamName,
1211
Type[] types,
1212
String[] names)
1213
throws IOException
1214
{
1215
if (types.length != names.length) {
1216
throw new Error("parameter type and name arrays different sizes");
1217
}
1218
1219
boolean readObject = false;
1220
for (int i = 0; i < types.length; i++) {
1221
if (writeUnmarshalArgument(p, streamName, types[i], names[i])) {
1222
readObject = true;
1223
}
1224
p.pln(";");
1225
}
1226
return readObject;
1227
}
1228
1229
/**
1230
* Return a snippet of Java code to wrap a value named "name" of
1231
* type "type" into an object as appropriate for use by the
1232
* Java Reflection API.
1233
*
1234
* For primitive types, an appropriate wrapper class instantiated
1235
* with the primitive value. For object types (including arrays),
1236
* no wrapping is necessary, so the value is named directly.
1237
*/
1238
private static String wrapArgumentCode(Type type, String name) {
1239
int typeCode = type.getTypeCode();
1240
switch (typeCode) {
1241
case TC_BOOLEAN:
1242
return ("(" + name +
1243
" ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)");
1244
case TC_BYTE:
1245
return "new java.lang.Byte(" + name + ")";
1246
case TC_CHAR:
1247
return "new java.lang.Character(" + name + ")";
1248
case TC_SHORT:
1249
return "new java.lang.Short(" + name + ")";
1250
case TC_INT:
1251
return "new java.lang.Integer(" + name + ")";
1252
case TC_LONG:
1253
return "new java.lang.Long(" + name + ")";
1254
case TC_FLOAT:
1255
return "new java.lang.Float(" + name + ")";
1256
case TC_DOUBLE:
1257
return "new java.lang.Double(" + name + ")";
1258
case TC_ARRAY:
1259
case TC_CLASS:
1260
return name;
1261
default:
1262
throw new Error("unexpected type code: " + typeCode);
1263
}
1264
}
1265
1266
/**
1267
* Return a snippet of Java code to unwrap a value named "name" into
1268
* a value of type "type", as appropriate for the Java Reflection API.
1269
*
1270
* For primitive types, the value is assumed to be of the corresponding
1271
* wrapper type, and a method is called on the wrapper type to retrieve
1272
* the primitive value. For object types (include arrays), no
1273
* unwrapping is necessary; the value is simply cast to the expected
1274
* real object type.
1275
*/
1276
private static String unwrapArgumentCode(Type type, String name) {
1277
int typeCode = type.getTypeCode();
1278
switch (typeCode) {
1279
case TC_BOOLEAN:
1280
return "((java.lang.Boolean) " + name + ").booleanValue()";
1281
case TC_BYTE:
1282
return "((java.lang.Byte) " + name + ").byteValue()";
1283
case TC_CHAR:
1284
return "((java.lang.Character) " + name + ").charValue()";
1285
case TC_SHORT:
1286
return "((java.lang.Short) " + name + ").shortValue()";
1287
case TC_INT:
1288
return "((java.lang.Integer) " + name + ").intValue()";
1289
case TC_LONG:
1290
return "((java.lang.Long) " + name + ").longValue()";
1291
case TC_FLOAT:
1292
return "((java.lang.Float) " + name + ").floatValue()";
1293
case TC_DOUBLE:
1294
return "((java.lang.Double) " + name + ").doubleValue()";
1295
case TC_ARRAY:
1296
case TC_CLASS:
1297
return "((" + type + ") " + name + ")";
1298
default:
1299
throw new Error("unexpected type code: " + typeCode);
1300
}
1301
}
1302
}
1303
1304