Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/rmic/Main.java
38831 views
/*1* Copyright (c) 1996, 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* Licensed Materials - Property of IBM27* RMI-IIOP v1.028* Copyright IBM Corp. 1998 1999 All Rights Reserved29*30*/3132package sun.rmi.rmic;3334import java.util.Vector;35import java.util.Enumeration;36import java.util.ResourceBundle;37import java.util.StringTokenizer;38import java.util.MissingResourceException;3940import java.io.OutputStream;41import java.io.PrintStream;42import java.io.IOException;43import java.io.File;44import java.io.FileNotFoundException;45import java.io.FileOutputStream;46import java.io.ByteArrayOutputStream;4748import sun.tools.java.ClassFile;49import sun.tools.java.ClassDefinition;50import sun.tools.java.ClassDeclaration;51import sun.tools.java.ClassNotFound;52import sun.tools.java.Identifier;53import sun.tools.java.ClassPath;5455import sun.tools.javac.SourceClass;56import sun.tools.util.CommandLine;57import java.lang.reflect.Constructor;58import java.util.Properties;5960/**61* Main "rmic" program.62*63* WARNING: The contents of this source file are not part of any64* supported API. Code that depends on them does so at its own risk:65* they are subject to change or removal without notice.66*/67public class Main implements sun.rmi.rmic.Constants {68String sourcePathArg;69String sysClassPathArg;70String extDirsArg;71String classPathString;72File destDir;73int flags;74long tm;75Vector<String> classes;76boolean nowrite;77boolean nocompile;78boolean keepGenerated;79boolean status;80String[] generatorArgs;81Vector<Generator> generators;82Class<? extends BatchEnvironment> environmentClass =83BatchEnvironment.class;84boolean iiopGeneration = false;8586/**87* Name of the program.88*/89String program;9091/**92* The stream where error message are printed.93*/94OutputStream out;9596/**97* Constructor.98*/99public Main(OutputStream out, String program) {100this.out = out;101this.program = program;102}103104/**105* Output a message.106*/107public void output(String msg) {108PrintStream out =109this.out instanceof PrintStream ? (PrintStream)this.out110: new PrintStream(this.out, true);111out.println(msg);112}113114/**115* Top level error message. This method is called when the116* environment could not be set up yet.117*/118public void error(String msg) {119output(getText(msg));120}121122public void error(String msg, String arg1) {123output(getText(msg, arg1));124}125126public void error(String msg, String arg1, String arg2) {127output(getText(msg, arg1, arg2));128}129130/**131* Usage132*/133public void usage() {134error("rmic.usage", program);135}136137/**138* Run the compiler139*/140public synchronized boolean compile(String argv[]) {141142/*143* Handle internal option to use the new (and incomplete) rmic144* implementation. This option is handled here, rather than145* in parseArgs, so that none of the arguments will be nulled146* before delegating to the new implementation.147*/148for (int i = 0; i < argv.length; i++) {149if (argv[i].equals("-Xnew")) {150return (new sun.rmi.rmic.newrmic.Main(out,151program)).compile(argv);152}153}154155if (!parseArgs(argv)) {156return false;157}158159if (classes.size() == 0) {160usage();161return false;162}163164if ((flags & F_WARNINGS) != 0) {165for (Generator g : generators) {166if (g instanceof RMIGenerator) {167output(getText("rmic.jrmp.stubs.deprecated", program));168break;169}170}171}172173return doCompile();174}175176/**177* Get the destination directory.178*/179public File getDestinationDir() {180return destDir;181}182183/**184* Parse the arguments for compile.185*/186public boolean parseArgs(String argv[]) {187sourcePathArg = null;188sysClassPathArg = null;189extDirsArg = null;190191classPathString = null;192destDir = null;193flags = F_WARNINGS;194tm = System.currentTimeMillis();195classes = new Vector<>();196nowrite = false;197nocompile = false;198keepGenerated = false;199generatorArgs = getArray("generator.args",true);200if (generatorArgs == null) {201return false;202}203generators = new Vector<>();204205// Pre-process command line for @file arguments206try {207argv = CommandLine.parse(argv);208} catch (FileNotFoundException e) {209error("rmic.cant.read", e.getMessage());210return false;211} catch (IOException e) {212e.printStackTrace(out instanceof PrintStream ?213(PrintStream) out :214new PrintStream(out, true));215return false;216}217218// Parse arguments219for (int i = 0 ; i < argv.length ; i++) {220if (argv[i] != null) {221if (argv[i].equals("-g")) {222flags &= ~F_OPT;223flags |= F_DEBUG_LINES | F_DEBUG_VARS;224argv[i] = null;225} else if (argv[i].equals("-O")) {226flags &= ~F_DEBUG_LINES;227flags &= ~F_DEBUG_VARS;228flags |= F_OPT | F_DEPENDENCIES;229argv[i] = null;230} else if (argv[i].equals("-nowarn")) {231flags &= ~F_WARNINGS;232argv[i] = null;233} else if (argv[i].equals("-debug")) {234flags |= F_DUMP;235argv[i] = null;236} else if (argv[i].equals("-depend")) {237flags |= F_DEPENDENCIES;238argv[i] = null;239} else if (argv[i].equals("-verbose")) {240flags |= F_VERBOSE;241argv[i] = null;242} else if (argv[i].equals("-nowrite")) {243nowrite = true;244argv[i] = null;245} else if (argv[i].equals("-Xnocompile")) {246nocompile = true;247keepGenerated = true;248argv[i] = null;249} else if (argv[i].equals("-keep") ||250argv[i].equals("-keepgenerated")) {251keepGenerated = true;252argv[i] = null;253} else if (argv[i].equals("-show")) {254error("rmic.option.unsupported", "-show");255usage();256return false;257} else if (argv[i].equals("-classpath")) {258if ((i + 1) < argv.length) {259if (classPathString != null) {260error("rmic.option.already.seen", "-classpath");261usage();262return false;263}264argv[i] = null;265classPathString = argv[++i];266argv[i] = null;267} else {268error("rmic.option.requires.argument", "-classpath");269usage();270return false;271}272} else if (argv[i].equals("-sourcepath")) {273if ((i + 1) < argv.length) {274if (sourcePathArg != null) {275error("rmic.option.already.seen", "-sourcepath");276usage();277return false;278}279argv[i] = null;280sourcePathArg = argv[++i];281argv[i] = null;282} else {283error("rmic.option.requires.argument", "-sourcepath");284usage();285return false;286}287} else if (argv[i].equals("-bootclasspath")) {288if ((i + 1) < argv.length) {289if (sysClassPathArg != null) {290error("rmic.option.already.seen", "-bootclasspath");291usage();292return false;293}294argv[i] = null;295sysClassPathArg = argv[++i];296argv[i] = null;297} else {298error("rmic.option.requires.argument", "-bootclasspath");299usage();300return false;301}302} else if (argv[i].equals("-extdirs")) {303if ((i + 1) < argv.length) {304if (extDirsArg != null) {305error("rmic.option.already.seen", "-extdirs");306usage();307return false;308}309argv[i] = null;310extDirsArg = argv[++i];311argv[i] = null;312} else {313error("rmic.option.requires.argument", "-extdirs");314usage();315return false;316}317} else if (argv[i].equals("-d")) {318if ((i + 1) < argv.length) {319if (destDir != null) {320error("rmic.option.already.seen", "-d");321usage();322return false;323}324argv[i] = null;325destDir = new File(argv[++i]);326argv[i] = null;327if (!destDir.exists()) {328error("rmic.no.such.directory", destDir.getPath());329usage();330return false;331}332} else {333error("rmic.option.requires.argument", "-d");334usage();335return false;336}337} else {338if (!checkGeneratorArg(argv,i)) {339usage();340return false;341}342}343}344}345346347// Now that all generators have had a chance at the args,348// scan what's left for classes and illegal args...349350for (int i = 0; i < argv.length; i++) {351if (argv[i] != null) {352if (argv[i].startsWith("-")) {353error("rmic.no.such.option", argv[i]);354usage();355return false;356} else {357classes.addElement(argv[i]);358}359}360}361362363// If the generators vector is empty, add the default generator...364365if (generators.size() == 0) {366addGenerator("default");367}368369return true;370}371372/**373* If this argument is for a generator, instantiate it, call374* parseArgs(...) and add generator to generators vector.375* Returns false on error.376*/377protected boolean checkGeneratorArg(String[] argv, int currentIndex) {378boolean result = true;379if (argv[currentIndex].startsWith("-")) {380String arg = argv[currentIndex].substring(1).toLowerCase(); // Remove '-'381for (int i = 0; i < generatorArgs.length; i++) {382if (arg.equalsIgnoreCase(generatorArgs[i])) {383// Got a match, add Generator and call parseArgs...384Generator gen = addGenerator(arg);385if (gen == null) {386return false;387}388result = gen.parseArgs(argv,this);389break;390}391}392}393return result;394}395396/**397* Instantiate and add a generator to the generators array.398*/399protected Generator addGenerator(String arg) {400401Generator gen;402403// Create an instance of the generator and add it to404// the array...405406String className = getString("generator.class." + arg);407if (className == null) {408error("rmic.missing.property",arg);409return null;410}411412try {413gen = (Generator) Class.forName(className).newInstance();414} catch (Exception e) {415error("rmic.cannot.instantiate",className);416return null;417}418419generators.addElement(gen);420421// Get the environment required by this generator...422423Class<?> envClass = BatchEnvironment.class;424String env = getString("generator.env." + arg);425if (env != null) {426try {427envClass = Class.forName(env);428429// Is the new class a subclass of the current one?430431if (environmentClass.isAssignableFrom(envClass)) {432433// Yes, so switch to the new one...434435environmentClass = envClass.asSubclass(BatchEnvironment.class);436437} else {438439// No. Is the current class a subclass of the440// new one?441442if (!envClass.isAssignableFrom(environmentClass)) {443444// No, so it's a conflict...445446error("rmic.cannot.use.both",environmentClass.getName(),envClass.getName());447return null;448}449}450} catch (ClassNotFoundException e) {451error("rmic.class.not.found",env);452return null;453}454}455456// If this is the iiop stub generator, cache457// that fact for the jrmp generator...458459if (arg.equals("iiop")) {460iiopGeneration = true;461}462return gen;463}464465/**466* Grab a resource string and parse it into an array of strings. Assumes467* comma separated list.468* @param name The resource name.469* @param mustExist If true, throws error if resource does not exist. If470* false and resource does not exist, returns zero element array.471*/472protected String[] getArray(String name, boolean mustExist) {473String[] result = null;474String value = getString(name);475if (value == null) {476if (mustExist) {477error("rmic.resource.not.found",name);478return null;479} else {480return new String[0];481}482}483484StringTokenizer parser = new StringTokenizer(value,", \t\n\r", false);485int count = parser.countTokens();486result = new String[count];487for (int i = 0; i < count; i++) {488result[i] = parser.nextToken();489}490491return result;492}493494/**495* Get the correct type of BatchEnvironment496*/497public BatchEnvironment getEnv() {498499ClassPath classPath =500BatchEnvironment.createClassPath(classPathString,501sysClassPathArg,502extDirsArg);503BatchEnvironment result = null;504try {505Class<?>[] ctorArgTypes = {OutputStream.class,ClassPath.class,Main.class};506Object[] ctorArgs = {out,classPath,this};507Constructor<? extends BatchEnvironment> constructor =508environmentClass.getConstructor(ctorArgTypes);509result = constructor.newInstance(ctorArgs);510result.reset();511}512catch (Exception e) {513error("rmic.cannot.instantiate",environmentClass.getName());514}515return result;516}517518519/**520* Do the compile with the switches and files already supplied521*/522public boolean doCompile() {523// Create batch environment524BatchEnvironment env = getEnv();525env.flags |= flags;526527// Set the classfile version numbers528// Compat and 1.1 stubs must retain the old version number.529env.majorVersion = 45;530env.minorVersion = 3;531532// Preload the "out of memory" error string just in case we run533// out of memory during the compile.534String noMemoryErrorString = getText("rmic.no.memory");535String stackOverflowErrorString = getText("rmic.stack.overflow");536537try {538/** Load the classes on the command line539* Replace the entries in classes with the ClassDefinition for the class540*/541for (int i = classes.size()-1; i >= 0; i-- ) {542Identifier implClassName =543Identifier.lookup(classes.elementAt(i));544545/*546* Fix bugid 4049354: support using '.' as an inner class547* qualifier on the command line (previously, only mangled548* inner class names were understood, like "pkg.Outer$Inner").549*550* The following method, also used by "javap", resolves the551* given unmangled inner class name to the appropriate552* internal identifier. For example, it translates553* "pkg.Outer.Inner" to "pkg.Outer. Inner".554*/555implClassName = env.resolvePackageQualifiedName(implClassName);556/*557* But if we use such an internal inner class name identifier558* to load the class definition, the Java compiler will notice559* if the impl class is a "private" inner class and then deny560* skeletons (needed unless "-v1.2" is used) the ability to561* cast to it. To work around this problem, we mangle inner562* class name identifiers to their binary "outer" class name:563* "pkg.Outer. Inner" becomes "pkg.Outer$Inner".564*/565implClassName = Names.mangleClass(implClassName);566567ClassDeclaration decl = env.getClassDeclaration(implClassName);568try {569ClassDefinition def = decl.getClassDefinition(env);570for (int j = 0; j < generators.size(); j++) {571Generator gen = generators.elementAt(j);572gen.generate(env, def, destDir);573}574} catch (ClassNotFound ex) {575env.error(0, "rmic.class.not.found", implClassName);576}577578}579580// compile all classes that need compilation581if (!nocompile) {582compileAllClasses(env);583}584} catch (OutOfMemoryError ee) {585// The compiler has run out of memory. Use the error string586// which we preloaded.587env.output(noMemoryErrorString);588return false;589} catch (StackOverflowError ee) {590env.output(stackOverflowErrorString);591return false;592} catch (Error ee) {593// We allow the compiler to take an exception silently if a program594// error has previously been detected. Presumably, this makes the595// compiler more robust in the face of bad error recovery.596if (env.nerrors == 0 || env.dump()) {597env.error(0, "fatal.error");598ee.printStackTrace(out instanceof PrintStream ?599(PrintStream) out :600new PrintStream(out, true));601}602} catch (Exception ee) {603if (env.nerrors == 0 || env.dump()) {604env.error(0, "fatal.exception");605ee.printStackTrace(out instanceof PrintStream ?606(PrintStream) out :607new PrintStream(out, true));608}609}610611env.flushErrors();612613boolean status = true;614if (env.nerrors > 0) {615String msg = "";616if (env.nerrors > 1) {617msg = getText("rmic.errors", env.nerrors);618} else {619msg = getText("rmic.1error");620}621if (env.nwarnings > 0) {622if (env.nwarnings > 1) {623msg += ", " + getText("rmic.warnings", env.nwarnings);624} else {625msg += ", " + getText("rmic.1warning");626}627}628output(msg);629status = false;630} else {631if (env.nwarnings > 0) {632if (env.nwarnings > 1) {633output(getText("rmic.warnings", env.nwarnings));634} else {635output(getText("rmic.1warning"));636}637}638}639640// last step is to delete generated source files641if (!keepGenerated) {642env.deleteGeneratedFiles();643}644645// We're done646if (env.verbose()) {647tm = System.currentTimeMillis() - tm;648output(getText("rmic.done_in", Long.toString(tm)));649}650651// Shutdown the environment object and release our resources.652// Note that while this is unneccessary when rmic is invoked653// the command line, there are environments in which rmic654// from is invoked within a server process, so resource655// reclamation is important...656657env.shutdown();658659sourcePathArg = null;660sysClassPathArg = null;661extDirsArg = null;662classPathString = null;663destDir = null;664classes = null;665generatorArgs = null;666generators = null;667environmentClass = null;668program = null;669out = null;670671return status;672}673674/*675* Compile all classes that need to be compiled.676*/677public void compileAllClasses (BatchEnvironment env)678throws ClassNotFound,679IOException,680InterruptedException {681ByteArrayOutputStream buf = new ByteArrayOutputStream(4096);682boolean done;683684do {685done = true;686for (Enumeration<?> e = env.getClasses() ; e.hasMoreElements() ; ) {687ClassDeclaration c = (ClassDeclaration)e.nextElement();688done = compileClass(c,buf,env);689}690} while (!done);691}692693/*694* Compile a single class.695* Fallthrough is intentional696*/697@SuppressWarnings("fallthrough")698public boolean compileClass (ClassDeclaration c,699ByteArrayOutputStream buf,700BatchEnvironment env)701throws ClassNotFound,702IOException,703InterruptedException {704boolean done = true;705env.flushErrors();706SourceClass src;707708switch (c.getStatus()) {709case CS_UNDEFINED:710{711if (!env.dependencies()) {712break;713}714// fall through715}716717case CS_SOURCE:718{719done = false;720env.loadDefinition(c);721if (c.getStatus() != CS_PARSED) {722break;723}724// fall through725}726727case CS_PARSED:728{729if (c.getClassDefinition().isInsideLocal()) {730break;731}732// If we get to here, then compilation is going733// to occur. If the -Xnocompile switch is set734// then fail. Note that this check is required735// here because this method is called from736// generators, not just from within this class...737738if (nocompile) {739throw new IOException("Compilation required, but -Xnocompile option in effect");740}741742done = false;743744src = (SourceClass)c.getClassDefinition(env);745src.check(env);746c.setDefinition(src, CS_CHECKED);747// fall through748}749750case CS_CHECKED:751{752src = (SourceClass)c.getClassDefinition(env);753// bail out if there were any errors754if (src.getError()) {755c.setDefinition(src, CS_COMPILED);756break;757}758done = false;759buf.reset();760src.compile(buf);761c.setDefinition(src, CS_COMPILED);762src.cleanup(env);763764if (src.getError() || nowrite) {765break;766}767768String pkgName = c.getName().getQualifier().toString().replace('.', File.separatorChar);769String className = c.getName().getFlatName().toString().replace('.', SIGC_INNERCLASS) + ".class";770771File file;772if (destDir != null) {773if (pkgName.length() > 0) {774file = new File(destDir, pkgName);775if (!file.exists()) {776file.mkdirs();777}778file = new File(file, className);779} else {780file = new File(destDir, className);781}782} else {783ClassFile classfile = (ClassFile)src.getSource();784if (classfile.isZipped()) {785env.error(0, "cant.write", classfile.getPath());786break;787}788file = new File(classfile.getPath());789file = new File(file.getParent(), className);790}791792// Create the file793try {794FileOutputStream out = new FileOutputStream(file.getPath());795buf.writeTo(out);796out.close();797if (env.verbose()) {798output(getText("rmic.wrote", file.getPath()));799}800} catch (IOException ee) {801env.error(0, "cant.write", file.getPath());802}803}804}805return done;806}807808/**809* Main program810*/811public static void main(String argv[]) {812Main compiler = new Main(System.out, "rmic");813System.exit(compiler.compile(argv) ? 0 : 1);814}815816/**817* Return the string value of a named resource in the rmic.properties818* resource bundle. If the resource is not found, null is returned.819*/820public static String getString(String key) {821if (!resourcesInitialized) {822initResources();823}824825// To enable extensions, search the 'resourcesExt'826// bundle first, followed by the 'resources' bundle...827828if (resourcesExt != null) {829try {830return resourcesExt.getString(key);831} catch (MissingResourceException e) {}832}833834try {835return resources.getString(key);836} catch (MissingResourceException ignore) {837}838return null;839}840841private static boolean resourcesInitialized = false;842private static ResourceBundle resources;843private static ResourceBundle resourcesExt = null;844845private static void initResources() {846try {847resources =848ResourceBundle.getBundle("sun.rmi.rmic.resources.rmic");849resourcesInitialized = true;850try {851resourcesExt =852ResourceBundle.getBundle("sun.rmi.rmic.resources.rmicext");853} catch (MissingResourceException e) {}854} catch (MissingResourceException e) {855throw new Error("fatal: missing resource bundle: " +856e.getClassName());857}858}859860public static String getText(String key) {861String message = getString(key);862if (message == null) {863message = "no text found: \"" + key + "\"";864}865return message;866}867868public static String getText(String key, int num) {869return getText(key, Integer.toString(num), null, null);870}871872public static String getText(String key, String arg0) {873return getText(key, arg0, null, null);874}875876public static String getText(String key, String arg0, String arg1) {877return getText(key, arg0, arg1, null);878}879880public static String getText(String key,881String arg0, String arg1, String arg2)882{883String format = getString(key);884if (format == null) {885format = "no text found: key = \"" + key + "\", " +886"arguments = \"{0}\", \"{1}\", \"{2}\"";887}888889String[] args = new String[3];890args[0] = (arg0 != null ? arg0 : "null");891args[1] = (arg1 != null ? arg1 : "null");892args[2] = (arg2 != null ? arg2 : "null");893894return java.text.MessageFormat.format(format, (Object[]) args);895}896}897898899