Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/rmic/RMIGenerator.java
38831 views
/*1* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*****************************************************************************/26/* Copyright (c) IBM Corporation 1998 */27/* */28/* (C) Copyright IBM Corp. 1998 */29/* */30/*****************************************************************************/3132package sun.rmi.rmic;3334import java.io.File;35import java.io.FileOutputStream;36import java.io.OutputStreamWriter;37import java.io.IOException;38import java.util.Enumeration;39import java.util.Hashtable;40import java.util.Vector;41import sun.tools.java.Type;42import sun.tools.java.Identifier;43import sun.tools.java.ClassDefinition;44import sun.tools.java.ClassDeclaration;45import sun.tools.java.ClassNotFound;46import sun.tools.java.ClassFile;47import sun.tools.java.MemberDefinition;48import com.sun.corba.se.impl.util.Utility;4950/**51* A Generator object will generate the Java source code of the stub52* and skeleton classes for an RMI remote implementation class, using53* a particular stub protocol version.54*55* WARNING: The contents of this source file are not part of any56* supported API. Code that depends on them does so at its own risk:57* they are subject to change or removal without notice.58*59* @author Peter Jones, Bryan Atsatt60*/61public class RMIGenerator implements RMIConstants, Generator {6263private static final Hashtable<String, Integer> versionOptions = new Hashtable<>();64static {65versionOptions.put("-v1.1", new Integer(STUB_VERSION_1_1));66versionOptions.put("-vcompat", new Integer(STUB_VERSION_FAT));67versionOptions.put("-v1.2", new Integer(STUB_VERSION_1_2));68}6970/**71* Default constructor for Main to use.72*/73public RMIGenerator() {74version = STUB_VERSION_1_2; // default is -v1.2 (see 4638155)75}7677/**78* Examine and consume command line arguments.79* @param argv The command line arguments. Ignore null80* and unknown arguments. Set each consumed argument to null.81* @param error Report any errors using the main.error() methods.82* @return true if no errors, false otherwise.83*/84public boolean parseArgs(String argv[], Main main) {85String explicitVersion = null;86for (int i = 0; i < argv.length; i++) {87if (argv[i] != null) {88String arg = argv[i].toLowerCase();89if (versionOptions.containsKey(arg)) {90if (explicitVersion != null &&91!explicitVersion.equals(arg))92{93main.error("rmic.cannot.use.both",94explicitVersion, arg);95return false;96}97explicitVersion = arg;98version = versionOptions.get(arg);99argv[i] = null;100}101}102}103return true;104}105106/**107* Generate the source files for the stub and/or skeleton classes108* needed by RMI for the given remote implementation class.109*110* @param env compiler environment111* @param cdef definition of remote implementation class112* to generate stubs and/or skeletons for113* @param destDir directory for the root of the package hierarchy114* for generated files115*/116public void generate(BatchEnvironment env, ClassDefinition cdef, File destDir) {117RemoteClass remoteClass = RemoteClass.forClass(env, cdef);118if (remoteClass == null) // exit if an error occurred119return;120121RMIGenerator gen;122try {123gen = new RMIGenerator(env, cdef, destDir, remoteClass, version);124} catch (ClassNotFound e) {125env.error(0, "rmic.class.not.found", e.name);126return;127}128gen.generate();129}130131private void generate() {132env.addGeneratedFile(stubFile);133134try {135IndentingWriter out = new IndentingWriter(136new OutputStreamWriter(new FileOutputStream(stubFile)));137writeStub(out);138out.close();139if (env.verbose()) {140env.output(Main.getText("rmic.wrote", stubFile.getPath()));141}142env.parseFile(new ClassFile(stubFile));143} catch (IOException e) {144env.error(0, "cant.write", stubFile.toString());145return;146}147148if (version == STUB_VERSION_1_1 ||149version == STUB_VERSION_FAT)150{151env.addGeneratedFile(skeletonFile);152153try {154IndentingWriter out = new IndentingWriter(155new OutputStreamWriter(156new FileOutputStream(skeletonFile)));157writeSkeleton(out);158out.close();159if (env.verbose()) {160env.output(Main.getText("rmic.wrote",161skeletonFile.getPath()));162}163env.parseFile(new ClassFile(skeletonFile));164} catch (IOException e) {165env.error(0, "cant.write", stubFile.toString());166return;167}168} else {169/*170* For bugid 4135136: if skeleton files are not being generated171* for this compilation run, delete old skeleton source or class172* files for this remote implementation class that were173* (presumably) left over from previous runs, to avoid user174* confusion from extraneous or inconsistent generated files.175*/176177File outputDir = Util.getOutputDirectoryFor(remoteClassName,destDir,env);178File skeletonClassFile = new File(outputDir,skeletonClassName.getName().toString() + ".class");179180skeletonFile.delete(); // ignore failures (no big deal)181skeletonClassFile.delete();182}183}184185/**186* Return the File object that should be used as the source file187* for the given Java class, using the supplied destination188* directory for the top of the package hierarchy.189*/190protected static File sourceFileForClass(Identifier className,191Identifier outputClassName,192File destDir,193BatchEnvironment env)194{195File packageDir = Util.getOutputDirectoryFor(className,destDir,env);196String outputName = Names.mangleClass(outputClassName).getName().toString();197198// Is there any existing _Tie equivalent leftover from a199// previous invocation of rmic -iiop? Only do this once per200// class by looking for skeleton generation...201202if (outputName.endsWith("_Skel")) {203String classNameStr = className.getName().toString();204File temp = new File(packageDir, Utility.tieName(classNameStr) + ".class");205if (temp.exists()) {206207// Found a tie. Is IIOP generation also being done?208209if (!env.getMain().iiopGeneration) {210211// No, so write a warning...212213env.error(0,"warn.rmic.tie.found",214classNameStr,215temp.getAbsolutePath());216}217}218}219220String outputFileName = outputName + ".java";221return new File(packageDir, outputFileName);222}223224225/** rmic environment for this object */226private BatchEnvironment env;227228/** the remote class that this instance is generating code for */229private RemoteClass remoteClass;230231/** version of the stub protocol to use in code generation */232private int version;233234/** remote methods for remote class, indexed by operation number */235private RemoteClass.Method[] remoteMethods;236237/**238* Names for the remote class and the stub and skeleton classes239* to be generated for it.240*/241private Identifier remoteClassName;242private Identifier stubClassName;243private Identifier skeletonClassName;244245private ClassDefinition cdef;246private File destDir;247private File stubFile;248private File skeletonFile;249250/**251* Names to use for the java.lang.reflect.Method static fields252* corresponding to each remote method.253*/254private String[] methodFieldNames;255256/** cached definition for certain exception classes in this environment */257private ClassDefinition defException;258private ClassDefinition defRemoteException;259private ClassDefinition defRuntimeException;260261/**262* Create a new stub/skeleton Generator object for the given263* remote implementation class to generate code according to264* the given stub protocol version.265*/266private RMIGenerator(BatchEnvironment env, ClassDefinition cdef,267File destDir, RemoteClass remoteClass, int version)268throws ClassNotFound269{270this.destDir = destDir;271this.cdef = cdef;272this.env = env;273this.remoteClass = remoteClass;274this.version = version;275276remoteMethods = remoteClass.getRemoteMethods();277278remoteClassName = remoteClass.getName();279stubClassName = Names.stubFor(remoteClassName);280skeletonClassName = Names.skeletonFor(remoteClassName);281282methodFieldNames = nameMethodFields(remoteMethods);283284stubFile = sourceFileForClass(remoteClassName,stubClassName, destDir , env);285skeletonFile = sourceFileForClass(remoteClassName,skeletonClassName, destDir, env);286287/*288* Initialize cached definitions for exception classes used289* in the generation process.290*/291defException =292env.getClassDeclaration(idJavaLangException).293getClassDefinition(env);294defRemoteException =295env.getClassDeclaration(idRemoteException).296getClassDefinition(env);297defRuntimeException =298env.getClassDeclaration(idJavaLangRuntimeException).299getClassDefinition(env);300}301302/**303* Write the stub for the remote class to a stream.304*/305private void writeStub(IndentingWriter p) throws IOException {306307/*308* Write boiler plate comment.309*/310p.pln("// Stub class generated by rmic, do not edit.");311p.pln("// Contents subject to change without notice.");312p.pln();313314/*315* If remote implementation class was in a particular package,316* declare the stub class to be in the same package.317*/318if (remoteClassName.isQualified()) {319p.pln("package " + remoteClassName.getQualifier() + ";");320p.pln();321}322323/*324* Declare the stub class; implement all remote interfaces.325*/326p.plnI("public final class " +327Names.mangleClass(stubClassName.getName()));328p.pln("extends " + idRemoteStub);329ClassDefinition[] remoteInterfaces = remoteClass.getRemoteInterfaces();330if (remoteInterfaces.length > 0) {331p.p("implements ");332for (int i = 0; i < remoteInterfaces.length; i++) {333if (i > 0)334p.p(", ");335p.p(remoteInterfaces[i].getName().toString());336}337p.pln();338}339p.pOlnI("{");340341if (version == STUB_VERSION_1_1 ||342version == STUB_VERSION_FAT)343{344writeOperationsArray(p);345p.pln();346writeInterfaceHash(p);347p.pln();348}349350if (version == STUB_VERSION_FAT ||351version == STUB_VERSION_1_2)352{353p.pln("private static final long serialVersionUID = " +354STUB_SERIAL_VERSION_UID + ";");355p.pln();356357/*358* We only need to declare and initialize the static fields of359* Method objects for each remote method if there are any remote360* methods; otherwise, skip this code entirely, to avoid generating361* a try/catch block for a checked exception that cannot occur362* (see bugid 4125181).363*/364if (methodFieldNames.length > 0) {365if (version == STUB_VERSION_FAT) {366p.pln("private static boolean useNewInvoke;");367}368writeMethodFieldDeclarations(p);369p.pln();370371/*372* Initialize java.lang.reflect.Method fields for each remote373* method in a static initializer.374*/375p.plnI("static {");376p.plnI("try {");377if (version == STUB_VERSION_FAT) {378/*379* Fat stubs must determine whether the API required for380* the JDK 1.2 stub protocol is supported in the current381* runtime, so that it can use it if supported. This is382* determined by using the Reflection API to test if the383* new invoke method on RemoteRef exists, and setting the384* static boolean "useNewInvoke" to true if it does, or385* to false if a NoSuchMethodException is thrown.386*/387p.plnI(idRemoteRef + ".class.getMethod(\"invoke\",");388p.plnI("new java.lang.Class[] {");389p.pln(idRemote + ".class,");390p.pln("java.lang.reflect.Method.class,");391p.pln("java.lang.Object[].class,");392p.pln("long.class");393p.pOln("});");394p.pO();395p.pln("useNewInvoke = true;");396}397writeMethodFieldInitializers(p);398p.pOlnI("} catch (java.lang.NoSuchMethodException e) {");399if (version == STUB_VERSION_FAT) {400p.pln("useNewInvoke = false;");401} else {402/*403* REMIND: By throwing an Error here, the application will404* get the NoSuchMethodError directly when the stub class405* is initialized. If we throw a RuntimeException406* instead, the application would get an407* ExceptionInInitializerError. Would that be more408* appropriate, and if so, which RuntimeException should409* be thrown?410*/411p.plnI("throw new java.lang.NoSuchMethodError(");412p.pln("\"stub class initialization failed\");");413p.pO();414}415p.pOln("}"); // end try/catch block416p.pOln("}"); // end static initializer417p.pln();418}419}420421writeStubConstructors(p);422p.pln();423424/*425* Write each stub method.426*/427if (remoteMethods.length > 0) {428p.pln("// methods from remote interfaces");429for (int i = 0; i < remoteMethods.length; ++i) {430p.pln();431writeStubMethod(p, i);432}433}434435p.pOln("}"); // end stub class436}437438/**439* Write the constructors for the stub class.440*/441private void writeStubConstructors(IndentingWriter p)442throws IOException443{444p.pln("// constructors");445446/*447* Only stubs compatible with the JDK 1.1 stub protocol need448* a no-arg constructor; later versions use reflection to find449* the constructor that directly takes a RemoteRef argument.450*/451if (version == STUB_VERSION_1_1 ||452version == STUB_VERSION_FAT)453{454p.plnI("public " + Names.mangleClass(stubClassName.getName()) +455"() {");456p.pln("super();");457p.pOln("}");458}459460p.plnI("public " + Names.mangleClass(stubClassName.getName()) +461"(" + idRemoteRef + " ref) {");462p.pln("super(ref);");463p.pOln("}");464}465466/**467* Write the stub method for the remote method with the given "opnum".468*/469private void writeStubMethod(IndentingWriter p, int opnum)470throws IOException471{472RemoteClass.Method method = remoteMethods[opnum];473Identifier methodName = method.getName();474Type methodType = method.getType();475Type paramTypes[] = methodType.getArgumentTypes();476String paramNames[] = nameParameters(paramTypes);477Type returnType = methodType.getReturnType();478ClassDeclaration[] exceptions = method.getExceptions();479480/*481* Declare stub method; throw exceptions declared in remote482* interface(s).483*/484p.pln("// implementation of " +485methodType.typeString(methodName.toString(), true, false));486p.p("public " + returnType + " " + methodName + "(");487for (int i = 0; i < paramTypes.length; i++) {488if (i > 0)489p.p(", ");490p.p(paramTypes[i] + " " + paramNames[i]);491}492p.plnI(")");493if (exceptions.length > 0) {494p.p("throws ");495for (int i = 0; i < exceptions.length; i++) {496if (i > 0)497p.p(", ");498p.p(exceptions[i].getName().toString());499}500p.pln();501}502p.pOlnI("{");503504/*505* The RemoteRef.invoke methods throw Exception, but unless this506* stub method throws Exception as well, we must catch Exceptions507* thrown from the invocation. So we must catch Exception and508* rethrow something we can throw: UnexpectedException, which is a509* subclass of RemoteException. But for any subclasses of Exception510* that we can throw, like RemoteException, RuntimeException, and511* any of the exceptions declared by this stub method, we want them512* to pass through unharmed, so first we must catch any such513* exceptions and rethrow it directly.514*515* We have to be careful generating the rethrowing catch blocks516* here, because javac will flag an error if there are any517* unreachable catch blocks, i.e. if the catch of an exception class518* follows a previous catch of it or of one of its superclasses.519* The following method invocation takes care of these details.520*/521Vector<ClassDefinition> catchList = computeUniqueCatchList(exceptions);522523/*524* If we need to catch any particular exceptions (i.e. this method525* does not declare java.lang.Exception), put the entire stub526* method in a try block.527*/528if (catchList.size() > 0) {529p.plnI("try {");530}531532if (version == STUB_VERSION_FAT) {533p.plnI("if (useNewInvoke) {");534}535if (version == STUB_VERSION_FAT ||536version == STUB_VERSION_1_2)537{538if (!returnType.isType(TC_VOID)) {539p.p("Object $result = "); // REMIND: why $?540}541p.p("ref.invoke(this, " + methodFieldNames[opnum] + ", ");542if (paramTypes.length > 0) {543p.p("new java.lang.Object[] {");544for (int i = 0; i < paramTypes.length; i++) {545if (i > 0)546p.p(", ");547p.p(wrapArgumentCode(paramTypes[i], paramNames[i]));548}549p.p("}");550} else {551p.p("null");552}553p.pln(", " + method.getMethodHash() + "L);");554if (!returnType.isType(TC_VOID)) {555p.pln("return " +556unwrapArgumentCode(returnType, "$result") + ";");557}558}559if (version == STUB_VERSION_FAT) {560p.pOlnI("} else {");561}562if (version == STUB_VERSION_1_1 ||563version == STUB_VERSION_FAT)564{565p.pln(idRemoteCall + " call = ref.newCall((" + idRemoteObject +566") this, operations, " + opnum + ", interfaceHash);");567568if (paramTypes.length > 0) {569p.plnI("try {");570p.pln("java.io.ObjectOutput out = call.getOutputStream();");571writeMarshalArguments(p, "out", paramTypes, paramNames);572p.pOlnI("} catch (java.io.IOException e) {");573p.pln("throw new " + idMarshalException +574"(\"error marshalling arguments\", e);");575p.pOln("}");576}577578p.pln("ref.invoke(call);");579580if (returnType.isType(TC_VOID)) {581p.pln("ref.done(call);");582} else {583p.pln(returnType + " $result;"); // REMIND: why $?584p.plnI("try {");585p.pln("java.io.ObjectInput in = call.getInputStream();");586boolean objectRead =587writeUnmarshalArgument(p, "in", returnType, "$result");588p.pln(";");589p.pOlnI("} catch (java.io.IOException e) {");590p.pln("throw new " + idUnmarshalException +591"(\"error unmarshalling return\", e);");592/*593* If any only if readObject has been invoked, we must catch594* ClassNotFoundException as well as IOException.595*/596if (objectRead) {597p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");598p.pln("throw new " + idUnmarshalException +599"(\"error unmarshalling return\", e);");600}601p.pOlnI("} finally {");602p.pln("ref.done(call);");603p.pOln("}");604p.pln("return $result;");605}606}607if (version == STUB_VERSION_FAT) {608p.pOln("}"); // end if/else (useNewInvoke) block609}610611/*612* If we need to catch any particular exceptions, finally write613* the catch blocks for them, rethrow any other Exceptions with an614* UnexpectedException, and end the try block.615*/616if (catchList.size() > 0) {617for (Enumeration<ClassDefinition> enumeration = catchList.elements();618enumeration.hasMoreElements();)619{620ClassDefinition def = enumeration.nextElement();621p.pOlnI("} catch (" + def.getName() + " e) {");622p.pln("throw e;");623}624p.pOlnI("} catch (java.lang.Exception e) {");625p.pln("throw new " + idUnexpectedException +626"(\"undeclared checked exception\", e);");627p.pOln("}"); // end try/catch block628}629630p.pOln("}"); // end stub method631}632633/**634* Compute the exceptions which need to be caught and rethrown in a635* stub method before wrapping Exceptions in UnexpectedExceptions,636* given the exceptions declared in the throws clause of the method.637* Returns a Vector containing ClassDefinition objects for each638* exception to catch. Each exception is guaranteed to be unique,639* i.e. not a subclass of any of the other exceptions in the Vector,640* so the catch blocks for these exceptions may be generated in any641* order relative to each other.642*643* RemoteException and RuntimeException are each automatically placed644* in the returned Vector (if none of their superclasses are already645* present), since those exceptions should always be directly rethrown646* by a stub method.647*648* The returned Vector will be empty if java.lang.Exception or one649* of its superclasses is in the throws clause of the method, indicating650* that no exceptions need to be caught.651*/652private Vector<ClassDefinition> computeUniqueCatchList(ClassDeclaration[] exceptions) {653Vector<ClassDefinition> uniqueList = new Vector<>(); // unique exceptions to catch654655uniqueList.addElement(defRuntimeException);656uniqueList.addElement(defRemoteException);657658/* For each exception declared by the stub method's throws clause: */659nextException:660for (int i = 0; i < exceptions.length; i++) {661ClassDeclaration decl = exceptions[i];662try {663if (defException.subClassOf(env, decl)) {664/*665* (If java.lang.Exception (or a superclass) was declared666* in the throws clause of this stub method, then we don't667* have to bother catching anything; clear the list and668* return.)669*/670uniqueList.clear();671break;672} else if (!defException.superClassOf(env, decl)) {673/*674* Ignore other Throwables that do not extend Exception,675* since they do not need to be caught anyway.676*/677continue;678}679/*680* Compare this exception against the current list of681* exceptions that need to be caught:682*/683for (int j = 0; j < uniqueList.size();) {684ClassDefinition def = uniqueList.elementAt(j);685if (def.superClassOf(env, decl)) {686/*687* If a superclass of this exception is already on688* the list to catch, then ignore and continue;689*/690continue nextException;691} else if (def.subClassOf(env, decl)) {692/*693* If a subclass of this exception is on the list694* to catch, then remove it.695*/696uniqueList.removeElementAt(j);697} else {698j++; // else continue comparing699}700}701/* This exception is unique: add it to the list to catch. */702uniqueList.addElement(decl.getClassDefinition(env));703} catch (ClassNotFound e) {704env.error(0, "class.not.found", e.name, decl.getName());705/*706* REMIND: We do not exit from this exceptional condition,707* generating questionable code and likely letting the708* compiler report a resulting error later.709*/710}711}712return uniqueList;713}714715/**716* Write the skeleton for the remote class to a stream.717*/718private void writeSkeleton(IndentingWriter p) throws IOException {719if (version == STUB_VERSION_1_2) {720throw new Error("should not generate skeleton for version");721}722723/*724* Write boiler plate comment.725*/726p.pln("// Skeleton class generated by rmic, do not edit.");727p.pln("// Contents subject to change without notice.");728p.pln();729730/*731* If remote implementation class was in a particular package,732* declare the skeleton class to be in the same package.733*/734if (remoteClassName.isQualified()) {735p.pln("package " + remoteClassName.getQualifier() + ";");736p.pln();737}738739/*740* Declare the skeleton class.741*/742p.plnI("public final class " +743Names.mangleClass(skeletonClassName.getName()));744p.pln("implements " + idSkeleton);745p.pOlnI("{");746747writeOperationsArray(p);748p.pln();749750writeInterfaceHash(p);751p.pln();752753/*754* Define the getOperations() method.755*/756p.plnI("public " + idOperation + "[] getOperations() {");757p.pln("return (" + idOperation + "[]) operations.clone();");758p.pOln("}");759p.pln();760761/*762* Define the dispatch() method.763*/764p.plnI("public void dispatch(" + idRemote + " obj, " +765idRemoteCall + " call, int opnum, long hash)");766p.pln("throws java.lang.Exception");767p.pOlnI("{");768769if (version == STUB_VERSION_FAT) {770p.plnI("if (opnum < 0) {");771if (remoteMethods.length > 0) {772for (int opnum = 0; opnum < remoteMethods.length; opnum++) {773if (opnum > 0)774p.pO("} else ");775p.plnI("if (hash == " +776remoteMethods[opnum].getMethodHash() + "L) {");777p.pln("opnum = " + opnum + ";");778}779p.pOlnI("} else {");780}781/*782* Skeleton throws UnmarshalException if it does not recognize783* the method hash; this is what UnicastServerRef.dispatch()784* would do.785*/786p.pln("throw new " +787idUnmarshalException + "(\"invalid method hash\");");788if (remoteMethods.length > 0) {789p.pOln("}");790}791/*792* Ignore the validation of the interface hash if the793* operation number was negative, since it is really a794* method hash instead.795*/796p.pOlnI("} else {");797}798799p.plnI("if (hash != interfaceHash)");800p.pln("throw new " +801idSkeletonMismatchException + "(\"interface hash mismatch\");");802p.pO();803804if (version == STUB_VERSION_FAT) {805p.pOln("}"); // end if/else (opnum < 0) block806}807p.pln();808809/*810* Cast remote object instance to our specific implementation class.811*/812p.pln(remoteClassName + " server = (" + remoteClassName + ") obj;");813814/*815* Process call according to the operation number.816*/817p.plnI("switch (opnum) {");818for (int opnum = 0; opnum < remoteMethods.length; opnum++) {819writeSkeletonDispatchCase(p, opnum);820}821p.pOlnI("default:");822/*823* Skeleton throws UnmarshalException if it does not recognize824* the operation number; this is consistent with the case of an825* unrecognized method hash.826*/827p.pln("throw new " + idUnmarshalException +828"(\"invalid method number\");");829p.pOln("}"); // end switch statement830831p.pOln("}"); // end dispatch() method832833p.pOln("}"); // end skeleton class834}835836/**837* Write the case block for the skeleton's dispatch method for838* the remote method with the given "opnum".839*/840private void writeSkeletonDispatchCase(IndentingWriter p, int opnum)841throws IOException842{843RemoteClass.Method method = remoteMethods[opnum];844Identifier methodName = method.getName();845Type methodType = method.getType();846Type paramTypes[] = methodType.getArgumentTypes();847String paramNames[] = nameParameters(paramTypes);848Type returnType = methodType.getReturnType();849850p.pOlnI("case " + opnum + ": // " +851methodType.typeString(methodName.toString(), true, false));852/*853* Use nested block statement inside case to provide an independent854* namespace for local variables used to unmarshal parameters for855* this remote method.856*/857p.pOlnI("{");858859if (paramTypes.length > 0) {860/*861* Declare local variables to hold arguments.862*/863for (int i = 0; i < paramTypes.length; i++) {864p.pln(paramTypes[i] + " " + paramNames[i] + ";");865}866867/*868* Unmarshal arguments from call stream.869*/870p.plnI("try {");871p.pln("java.io.ObjectInput in = call.getInputStream();");872boolean objectsRead = writeUnmarshalArguments(p, "in",873paramTypes, paramNames);874p.pOlnI("} catch (java.io.IOException e) {");875p.pln("throw new " + idUnmarshalException +876"(\"error unmarshalling arguments\", e);");877/*878* If any only if readObject has been invoked, we must catch879* ClassNotFoundException as well as IOException.880*/881if (objectsRead) {882p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");883p.pln("throw new " + idUnmarshalException +884"(\"error unmarshalling arguments\", e);");885}886p.pOlnI("} finally {");887p.pln("call.releaseInputStream();");888p.pOln("}");889} else {890p.pln("call.releaseInputStream();");891}892893if (!returnType.isType(TC_VOID)) {894/*895* Declare variable to hold return type, if not void.896*/897p.p(returnType + " $result = "); // REMIND: why $?898}899900/*901* Invoke the method on the server object.902*/903p.p("server." + methodName + "(");904for (int i = 0; i < paramNames.length; i++) {905if (i > 0)906p.p(", ");907p.p(paramNames[i]);908}909p.pln(");");910911/*912* Always invoke getResultStream(true) on the call object to send913* the indication of a successful invocation to the caller. If914* the return type is not void, keep the result stream and marshal915* the return value.916*/917p.plnI("try {");918if (!returnType.isType(TC_VOID)) {919p.p("java.io.ObjectOutput out = ");920}921p.pln("call.getResultStream(true);");922if (!returnType.isType(TC_VOID)) {923writeMarshalArgument(p, "out", returnType, "$result");924p.pln(";");925}926p.pOlnI("} catch (java.io.IOException e) {");927p.pln("throw new " +928idMarshalException + "(\"error marshalling return\", e);");929p.pOln("}");930931p.pln("break;"); // break from switch statement932933p.pOlnI("}"); // end nested block statement934p.pln();935}936937/**938* Write declaration and initializer for "operations" static array.939*/940private void writeOperationsArray(IndentingWriter p)941throws IOException942{943p.plnI("private static final " + idOperation + "[] operations = {");944for (int i = 0; i < remoteMethods.length; i++) {945if (i > 0)946p.pln(",");947p.p("new " + idOperation + "(\"" +948remoteMethods[i].getOperationString() + "\")");949}950p.pln();951p.pOln("};");952}953954/**955* Write declaration and initializer for "interfaceHash" static field.956*/957private void writeInterfaceHash(IndentingWriter p)958throws IOException959{960p.pln("private static final long interfaceHash = " +961remoteClass.getInterfaceHash() + "L;");962}963964/**965* Write declaration for java.lang.reflect.Method static fields966* corresponding to each remote method in a stub.967*/968private void writeMethodFieldDeclarations(IndentingWriter p)969throws IOException970{971for (int i = 0; i < methodFieldNames.length; i++) {972p.pln("private static java.lang.reflect.Method " +973methodFieldNames[i] + ";");974}975}976977/**978* Write code to initialize the static fields for each method979* using the Java Reflection API.980*/981private void writeMethodFieldInitializers(IndentingWriter p)982throws IOException983{984for (int i = 0; i < methodFieldNames.length; i++) {985p.p(methodFieldNames[i] + " = ");986/*987* Here we look up the Method object in the arbitrary interface988* that we find in the RemoteClass.Method object.989* REMIND: Is this arbitrary choice OK?990* REMIND: Should this access be part of RemoteClass.Method's991* abstraction?992*/993RemoteClass.Method method = remoteMethods[i];994MemberDefinition def = method.getMemberDefinition();995Identifier methodName = method.getName();996Type methodType = method.getType();997Type paramTypes[] = methodType.getArgumentTypes();998999p.p(def.getClassDefinition().getName() + ".class.getMethod(\"" +1000methodName + "\", new java.lang.Class[] {");1001for (int j = 0; j < paramTypes.length; j++) {1002if (j > 0)1003p.p(", ");1004p.p(paramTypes[j] + ".class");1005}1006p.pln("});");1007}1008}100910101011/*1012* Following are a series of static utility methods useful during1013* the code generation process:1014*/10151016/**1017* Generate an array of names for fields that correspond to the given1018* array of remote methods. Each name in the returned array is1019* guaranteed to be unique.1020*1021* The name of a method is included in its corresponding field name1022* to enhance readability of the generated code.1023*/1024private static String[] nameMethodFields(RemoteClass.Method[] methods) {1025String[] names = new String[methods.length];1026for (int i = 0; i < names.length; i++) {1027names[i] = "$method_" + methods[i].getName() + "_" + i;1028}1029return names;1030}10311032/**1033* Generate an array of names for parameters corresponding to the1034* given array of types for the parameters. Each name in the returned1035* array is guaranteed to be unique.1036*1037* A representation of the type of a parameter is included in its1038* corresponding field name to enhance the readability of the generated1039* code.1040*/1041private static String[] nameParameters(Type[] types) {1042String[] names = new String[types.length];1043for (int i = 0; i < names.length; i++) {1044names[i] = "$param_" +1045generateNameFromType(types[i]) + "_" + (i + 1);1046}1047return names;1048}10491050/**1051* Generate a readable string representing the given type suitable1052* for embedding within a Java identifier.1053*/1054private static String generateNameFromType(Type type) {1055int typeCode = type.getTypeCode();1056switch (typeCode) {1057case TC_BOOLEAN:1058case TC_BYTE:1059case TC_CHAR:1060case TC_SHORT:1061case TC_INT:1062case TC_LONG:1063case TC_FLOAT:1064case TC_DOUBLE:1065return type.toString();1066case TC_ARRAY:1067return "arrayOf_" + generateNameFromType(type.getElementType());1068case TC_CLASS:1069return Names.mangleClass(type.getClassName().getName()).toString();1070default:1071throw new Error("unexpected type code: " + typeCode);1072}1073}10741075/**1076* Write a snippet of Java code to marshal a value named "name" of1077* type "type" to the java.io.ObjectOutput stream named "stream".1078*1079* Primitive types are marshalled with their corresponding methods1080* in the java.io.DataOutput interface, and objects (including arrays)1081* are marshalled using the writeObject method.1082*/1083private static void writeMarshalArgument(IndentingWriter p,1084String streamName,1085Type type, String name)1086throws IOException1087{1088int typeCode = type.getTypeCode();1089switch (typeCode) {1090case TC_BOOLEAN:1091p.p(streamName + ".writeBoolean(" + name + ")");1092break;1093case TC_BYTE:1094p.p(streamName + ".writeByte(" + name + ")");1095break;1096case TC_CHAR:1097p.p(streamName + ".writeChar(" + name + ")");1098break;1099case TC_SHORT:1100p.p(streamName + ".writeShort(" + name + ")");1101break;1102case TC_INT:1103p.p(streamName + ".writeInt(" + name + ")");1104break;1105case TC_LONG:1106p.p(streamName + ".writeLong(" + name + ")");1107break;1108case TC_FLOAT:1109p.p(streamName + ".writeFloat(" + name + ")");1110break;1111case TC_DOUBLE:1112p.p(streamName + ".writeDouble(" + name + ")");1113break;1114case TC_ARRAY:1115case TC_CLASS:1116p.p(streamName + ".writeObject(" + name + ")");1117break;1118default:1119throw new Error("unexpected type code: " + typeCode);1120}1121}11221123/**1124* Write Java statements to marshal a series of values in order as1125* named in the "names" array, with types as specified in the "types"1126* array", to the java.io.ObjectOutput stream named "stream".1127*/1128private static void writeMarshalArguments(IndentingWriter p,1129String streamName,1130Type[] types, String[] names)1131throws IOException1132{1133if (types.length != names.length) {1134throw new Error("parameter type and name arrays different sizes");1135}11361137for (int i = 0; i < types.length; i++) {1138writeMarshalArgument(p, streamName, types[i], names[i]);1139p.pln(";");1140}1141}11421143/**1144* Write a snippet of Java code to unmarshal a value of type "type"1145* from the java.io.ObjectInput stream named "stream" into a variable1146* named "name" (if "name" is null, the value in unmarshalled and1147* discarded).1148*1149* Primitive types are unmarshalled with their corresponding methods1150* in the java.io.DataInput interface, and objects (including arrays)1151* are unmarshalled using the readObject method.1152*/1153private static boolean writeUnmarshalArgument(IndentingWriter p,1154String streamName,1155Type type, String name)1156throws IOException1157{1158boolean readObject = false;11591160if (name != null) {1161p.p(name + " = ");1162}11631164int typeCode = type.getTypeCode();1165switch (type.getTypeCode()) {1166case TC_BOOLEAN:1167p.p(streamName + ".readBoolean()");1168break;1169case TC_BYTE:1170p.p(streamName + ".readByte()");1171break;1172case TC_CHAR:1173p.p(streamName + ".readChar()");1174break;1175case TC_SHORT:1176p.p(streamName + ".readShort()");1177break;1178case TC_INT:1179p.p(streamName + ".readInt()");1180break;1181case TC_LONG:1182p.p(streamName + ".readLong()");1183break;1184case TC_FLOAT:1185p.p(streamName + ".readFloat()");1186break;1187case TC_DOUBLE:1188p.p(streamName + ".readDouble()");1189break;1190case TC_ARRAY:1191case TC_CLASS:1192p.p("(" + type + ") " + streamName + ".readObject()");1193readObject = true;1194break;1195default:1196throw new Error("unexpected type code: " + typeCode);1197}1198return readObject;1199}12001201/**1202* Write Java statements to unmarshal a series of values in order of1203* types as in the "types" array from the java.io.ObjectInput stream1204* named "stream" into variables as named in "names" (for any element1205* of "names" that is null, the corresponding value is unmarshalled1206* and discarded).1207*/1208private static boolean writeUnmarshalArguments(IndentingWriter p,1209String streamName,1210Type[] types,1211String[] names)1212throws IOException1213{1214if (types.length != names.length) {1215throw new Error("parameter type and name arrays different sizes");1216}12171218boolean readObject = false;1219for (int i = 0; i < types.length; i++) {1220if (writeUnmarshalArgument(p, streamName, types[i], names[i])) {1221readObject = true;1222}1223p.pln(";");1224}1225return readObject;1226}12271228/**1229* Return a snippet of Java code to wrap a value named "name" of1230* type "type" into an object as appropriate for use by the1231* Java Reflection API.1232*1233* For primitive types, an appropriate wrapper class instantiated1234* with the primitive value. For object types (including arrays),1235* no wrapping is necessary, so the value is named directly.1236*/1237private static String wrapArgumentCode(Type type, String name) {1238int typeCode = type.getTypeCode();1239switch (typeCode) {1240case TC_BOOLEAN:1241return ("(" + name +1242" ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)");1243case TC_BYTE:1244return "new java.lang.Byte(" + name + ")";1245case TC_CHAR:1246return "new java.lang.Character(" + name + ")";1247case TC_SHORT:1248return "new java.lang.Short(" + name + ")";1249case TC_INT:1250return "new java.lang.Integer(" + name + ")";1251case TC_LONG:1252return "new java.lang.Long(" + name + ")";1253case TC_FLOAT:1254return "new java.lang.Float(" + name + ")";1255case TC_DOUBLE:1256return "new java.lang.Double(" + name + ")";1257case TC_ARRAY:1258case TC_CLASS:1259return name;1260default:1261throw new Error("unexpected type code: " + typeCode);1262}1263}12641265/**1266* Return a snippet of Java code to unwrap a value named "name" into1267* a value of type "type", as appropriate for the Java Reflection API.1268*1269* For primitive types, the value is assumed to be of the corresponding1270* wrapper type, and a method is called on the wrapper type to retrieve1271* the primitive value. For object types (include arrays), no1272* unwrapping is necessary; the value is simply cast to the expected1273* real object type.1274*/1275private static String unwrapArgumentCode(Type type, String name) {1276int typeCode = type.getTypeCode();1277switch (typeCode) {1278case TC_BOOLEAN:1279return "((java.lang.Boolean) " + name + ").booleanValue()";1280case TC_BYTE:1281return "((java.lang.Byte) " + name + ").byteValue()";1282case TC_CHAR:1283return "((java.lang.Character) " + name + ").charValue()";1284case TC_SHORT:1285return "((java.lang.Short) " + name + ").shortValue()";1286case TC_INT:1287return "((java.lang.Integer) " + name + ").intValue()";1288case TC_LONG:1289return "((java.lang.Long) " + name + ").longValue()";1290case TC_FLOAT:1291return "((java.lang.Float) " + name + ").floatValue()";1292case TC_DOUBLE:1293return "((java.lang.Double) " + name + ").doubleValue()";1294case TC_ARRAY:1295case TC_CLASS:1296return "((" + type + ") " + name + ")";1297default:1298throw new Error("unexpected type code: " + typeCode);1299}1300}1301}130213031304