001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm;
014    
015    import java.io.File;
016    import java.util.Arrays;
017    import org.jikesrvm.adaptive.controller.Controller;
018    import org.jikesrvm.classloader.RVMClassLoader;
019    import org.jikesrvm.compilers.baseline.BaselineCompiler;
020    import org.jikesrvm.compilers.baseline.BaselineOptions;
021    import org.jikesrvm.compilers.common.RuntimeCompiler;
022    import org.jikesrvm.mm.mminterface.MemoryManager;
023    
024    import static org.jikesrvm.runtime.SysCall.sysCall;
025    import org.jikesrvm.scheduler.RVMThread;
026    
027    /**
028     * Command line option processing.
029     *
030     *      Arbitrary prefix support
031     */
032    public class CommandLineArgs {
033      private static final boolean DEBUG = false;
034    
035      /**
036       * Argument types
037       */
038      private enum PrefixType {
039        /**
040         * Invalid argument type
041         */
042        INVALID_ARG, // default
043        /**
044         * Application argument
045         */
046        APPLICATION_ARG,
047    
048        // -----------------------------------------------//
049        // The following arguments are standard java.     //
050        // -----------------------------------------------//
051        CLASSPATH_ARG,
052        ENVIRONMENT_ARG,
053        VERBOSE_JNI_ARG,
054        VERBOSE_CLS_ARG,
055        JAR_ARG,
056        JAVAAGENT_ARG,
057        ENABLE_ASSERTION_ARG,
058        ENABLE_SYSTEM_ASSERTION_ARG,
059        DISABLE_ASSERTION_ARG,
060        DISABLE_SYSTEM_ASSERTION_ARG,
061    
062        // -----------------------------------------------//
063        // The following arguments are RVM-specific.      //
064        // -----------------------------------------------//
065        HELP_ARG,
066        ARG,
067        IRC_HELP_ARG,
068        IRC_ARG,
069        RECOMP_HELP_ARG,
070        RECOMP_ARG,
071        AOS_HELP_ARG,
072        AOS_ARG,
073        BASE_HELP_ARG,
074        BASE_ARG,
075        OPT_ARG,
076        OPT_HELP_ARG,
077        /* Silently ignored */
078        VERIFY_ARG,
079        GC_HELP_ARG,
080        GC_ARG,
081        BOOTCLASSPATH_P_ARG,
082        BOOTCLASSPATH_A_ARG,
083        BOOTSTRAP_CLASSES_ARG,
084        PROCESSORS_ARG
085      }
086    
087      /** Represent a single command line prefix */
088      private static final class Prefix implements Comparable<Prefix> {
089        /** The command line string e.g. "-X:irc:" */
090        public final String value;
091        /** A number that describes the type of the argument */
092        public final PrefixType type;
093        /** Number of arguments of this type seen */
094        public int count = 0;
095    
096        /** Construct a prefix with the given argument string and type */
097        public Prefix(String v, PrefixType t) {
098          value = v;
099          type = t;
100          if (t == null) {
101            throw new Error("Type of prefix should never be null");
102          }
103        }
104    
105        /** Sorting method for Comparable. Sort by string value */
106        public int compareTo(Prefix o) {
107          return -value.compareTo(o.value);
108        }
109        /** Equals method to be consistent with Comparable */
110        public boolean equals(Object o) {
111          if (o instanceof Prefix) {
112            return value.equals(((Prefix)o).value);
113          }
114          return false;
115        }
116        /** Hashcode to be consistent with Comparable */
117        public int hashCode() {
118          return value.hashCode();
119        }
120        /** Command line string representation of the prefix */
121        public String toString() {
122          return value;
123        }
124      }
125    
126      /**
127       * A catch-all prefix to find application name.
128       */
129      private static final Prefix app_prefix = new Prefix("", PrefixType.APPLICATION_ARG);
130    
131      /**
132       * A list of possible prefixes for command line arguments.
133       * Each argument will be classified by the prefix it matches.
134       * One prefix can contain another.
135       *
136       * Prefixes are normally matched with the start of the argument.
137       * If the last character of the prefix is a '$', the prefix (without the
138       * trailing '$') will be matched with the whole argument.
139       * If the last character of the prefix is a ' ' (space), the prefix
140       * (without the trailing ' ') will be matched with the whole argument,
141       * and the next argument will be appended to the end of the one matching
142       * the prefix, with a space in between.
143       *
144       * The type will be used to classify the prefix.  Multiple entries CAN
145       * have the same type.
146       */
147      private static final Prefix[] prefixes = {new Prefix("-classpath ", PrefixType.CLASSPATH_ARG),
148                                                // Note: space is significant
149                                                new Prefix("-cp ", PrefixType.CLASSPATH_ARG),
150                                                // Note: space is significant
151                                                new Prefix("-jar ", PrefixType.JAR_ARG),
152                                                // Note: space is significant
153                                                new Prefix("-javaagent:", PrefixType.JAVAAGENT_ARG),
154                                                new Prefix("-D", PrefixType.ENVIRONMENT_ARG),
155                                                new Prefix("-verbose:class$", PrefixType.VERBOSE_CLS_ARG),
156                                                new Prefix("-verbose:jni$", PrefixType.VERBOSE_JNI_ARG),
157                                                new Prefix("-verbose$", PrefixType.VERBOSE_CLS_ARG),
158    
159                                                new Prefix("-enableassertions:", PrefixType.ENABLE_ASSERTION_ARG),
160                                                new Prefix("-ea:", PrefixType.ENABLE_ASSERTION_ARG),
161                                                new Prefix("-enableassertions:", PrefixType.ENABLE_ASSERTION_ARG),
162                                                new Prefix("-ea", PrefixType.ENABLE_ASSERTION_ARG),
163    
164                                                new Prefix("-enableassertions", PrefixType.ENABLE_ASSERTION_ARG),
165    
166                                                new Prefix("-esa:", PrefixType.ENABLE_SYSTEM_ASSERTION_ARG),
167                                                new Prefix("-enablesystemassertions:", PrefixType.ENABLE_SYSTEM_ASSERTION_ARG),
168                                                new Prefix("-esa", PrefixType.ENABLE_SYSTEM_ASSERTION_ARG),
169                                                new Prefix("-enablesystemassertions", PrefixType.ENABLE_SYSTEM_ASSERTION_ARG),
170    
171                                                new Prefix("-disableassertions:", PrefixType.DISABLE_ASSERTION_ARG),
172                                                new Prefix("-da:", PrefixType.DISABLE_ASSERTION_ARG),
173                                                new Prefix("-disableassertions", PrefixType.DISABLE_ASSERTION_ARG),
174                                                new Prefix("-da", PrefixType.DISABLE_ASSERTION_ARG),
175    
176                                                new Prefix("-disablesystemassertions:", PrefixType.DISABLE_SYSTEM_ASSERTION_ARG),
177                                                new Prefix("-dsa:", PrefixType.DISABLE_SYSTEM_ASSERTION_ARG),
178                                                new Prefix("-disablesystemassertions", PrefixType.DISABLE_SYSTEM_ASSERTION_ARG),
179                                                new Prefix("-dsa", PrefixType.DISABLE_SYSTEM_ASSERTION_ARG),
180    
181                                                new Prefix("-Xbootclasspath/p:", PrefixType.BOOTCLASSPATH_P_ARG),
182                                                new Prefix("-Xbootclasspath/a:", PrefixType.BOOTCLASSPATH_A_ARG),
183                                                new Prefix("-X:vmClasses=", PrefixType.BOOTSTRAP_CLASSES_ARG),
184                                                new Prefix("-X:processors=", PrefixType.PROCESSORS_ARG),
185                                                new Prefix("-X:irc:help$", PrefixType.IRC_HELP_ARG),
186                                                new Prefix("-X:irc$", PrefixType.IRC_HELP_ARG),
187                                                new Prefix("-X:irc:", PrefixType.IRC_ARG),
188                                                new Prefix("-X:recomp:help$", PrefixType.RECOMP_HELP_ARG),
189                                                new Prefix("-X:recomp$", PrefixType.RECOMP_HELP_ARG),
190                                                new Prefix("-X:recomp", PrefixType.RECOMP_ARG),
191                                                new Prefix("-X:aos:help$", PrefixType.AOS_HELP_ARG),
192                                                new Prefix("-X:aos$", PrefixType.AOS_HELP_ARG),
193                                                new Prefix("-X:aos:", PrefixType.AOS_ARG),
194                                                new Prefix("-X:gc:help$", PrefixType.GC_HELP_ARG),
195                                                new Prefix("-X:gc$", PrefixType.GC_HELP_ARG),
196                                                new Prefix("-X:gc:", PrefixType.GC_ARG),
197                                                new Prefix("-X:base:help$", PrefixType.BASE_HELP_ARG),
198                                                new Prefix("-X:base$", PrefixType.BASE_HELP_ARG),
199                                                new Prefix("-X:base:", PrefixType.BASE_ARG),
200                                                new Prefix("-X:opt:help$", PrefixType.OPT_HELP_ARG),
201                                                new Prefix("-X:opt$", PrefixType.OPT_HELP_ARG),
202                                                new Prefix("-X:opt:", PrefixType.OPT_ARG),
203                                                new Prefix("-X:vm:help$", PrefixType.HELP_ARG),
204                                                new Prefix("-X:vm$", PrefixType.HELP_ARG),
205                                                new Prefix("-X:vm:", PrefixType.ARG),
206    
207                                                /* Silently ignored */
208                                                new Prefix("-Xverify", PrefixType.VERIFY_ARG),
209    
210                                                app_prefix};
211    
212      static {
213        Arrays.sort(prefixes);
214        if (DEBUG) {
215          for (int i = 0; i < prefixes.length; i++) {
216            Prefix t = prefixes[i];
217            VM.sysWrite("Prefix[" + i + "]: \"" + t.value + "\"; " + t.type + "\n");
218          }
219        }
220      }
221    
222      /**
223       * The command line arguments.
224       */
225      private static String[] args;
226      /**
227       * The types of each command line argument.
228       */
229      private static PrefixType[] arg_types;
230      /**
231       * The position of application class name.
232       */
233      private static int app_name_pos = -1;
234    
235      /**
236       * Fetch arguments from program command line.
237       */
238      static void fetchCommandLineArguments() {
239        if (args != null) {
240          // if already been here...
241          return;
242        }
243        ArgReader argRdr = new ArgReader();
244    
245        int numArgs = argRdr.numArgs();
246        args = new String[numArgs];
247        arg_types = new PrefixType[numArgs];
248    
249        for (int i = 0; i < numArgs; ++i) {
250          String arg = argRdr.getArg(i);
251    
252          if (app_prefix.count > 0) {
253            /* We're already into the application arguments.  Here's another
254             * one. */
255            args[i] = arg;
256            arg_types[i] = PrefixType.APPLICATION_ARG;
257            app_prefix.count++;
258            continue;
259          }
260    
261          // Note: This loop will never run to the end.
262          for (Prefix p : prefixes) {
263            String v = p.value;
264            if (!matches(arg, v)) {
265              continue;
266            }
267            // Chop off the prefix (which we've already matched) and store the
268            // value portion of the string (the unique part) in args[i].  Store
269            // information about the prefix itself in arg_types[i].
270            args[i] = arg.substring(length(v));
271            if (DEBUG) {
272              VM.sysWrite("length(v) = ");
273              VM.sysWrite(length(v));
274    
275              VM.sysWrite("; v = \"");
276              VM.sysWrite(v);
277              VM.sysWriteln("\"");
278              VM.sysWrite("args[");
279              VM.sysWrite(i);
280              VM.sysWrite("] = \"");
281              VM.sysWrite(args[i]);
282              VM.sysWrite("\"; arg = \"");
283              VM.sysWrite(arg);
284              VM.sysWriteln("\"");
285            }
286    
287            arg_types[i] = p.type;
288            p = findPrefix(p.type); // Find the canonical prefix for this type...
289            p.count++;              // And increment the usage count for that
290            // canonical prefix.
291            if (v.endsWith(" ")) {
292              if (++i >= numArgs) {
293                VM.sysWriteln("vm: ", v, "needs an argument");
294                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
295              }
296              args[i - 1] += argRdr.getArg(i);
297              args[i] = null;
298            }
299            if (p == app_prefix) {
300              app_name_pos = i;
301            }
302            break;
303          }
304        } // for (i = 0; i < numArgs...)
305        /*
306         * If no application is specified, set app_name_pos to numArgs (that is,
307         * to one past the last item in the array of arguments) to ensure all
308         * command-line arguments are processed.
309         */
310        if (app_name_pos == -1) {
311          app_name_pos = numArgs;
312        }
313      }
314    
315      /**
316       * Does the argument match the prefix?
317       * @param arg argument
318       * @param p prefix
319       * @return true if argument "matches" the prefix, false otherwise
320       */
321      private static boolean matches(String arg, String p) {
322        if (p.endsWith(" ")) {
323          return arg.equals(p.substring(0, p.length() - 1)) || arg.startsWith(p);
324        }
325        if (p.endsWith("$")) {
326          return arg.equals(p.substring(0, p.length() - 1));
327        }
328        return arg.startsWith(p);
329      }
330    
331      /**
332       * The real length of the prefix.
333       * @param p prefix
334       * @return real length of prefix
335       */
336      private static int length(String p) {
337        if (p.endsWith("$") || p.endsWith(" ")) return p.length() - 1;
338        return p.length();
339      }
340    
341      /**
342       * Find a Prefix object of a given type.
343       */
344      private static Prefix findPrefix(PrefixType type) {
345        for (Prefix prefix : prefixes) if (prefix.type == type) return prefix;
346        return null;
347      }
348    
349      /**
350       * Extract all command line arguments of a particular type.
351       * Strips out the prefixes (if any).
352       * !!TODO: cache results instead of constructing a new array each time.
353       * @param prefix type of arguments to extract
354       * @return array of arguments or null if type is invalid
355       */
356      public static String[] getArgs(PrefixType prefix) {
357        String[] retarg = null;
358        Prefix p = findPrefix(prefix);
359        if (p != null) {
360          retarg = new String[p.count];
361          for (int i = 0, j = 0; i < args.length; i++) {
362            if (arg_types[i] == prefix) {
363              retarg[j++] = args[i];
364            }
365          }
366        }
367        return retarg;
368      }
369    
370      /**
371       * Extract command line arguments for the Java agent
372       * @return Java agent arguments
373       */
374      public static String[] getJavaAgentArgs() {
375        return CommandLineArgs.getArgs(CommandLineArgs.PrefixType.JAVAAGENT_ARG);
376      }
377    
378      /**
379       * Get all environment arguments as pairs of string of key followed by value
380       */
381      public static String[] getEnvironmentArgs() {
382        if (!VM.runningVM) throw new IllegalAccessError("Environment variables can't be read in a non-running VM");
383        return getArgs(PrefixType.ENVIRONMENT_ARG);
384      }
385    
386      /**
387       * Extract the first -D... command line argument that matches a given
388       * variable, and return it.
389       * @return the environment arg, or null if there is none.
390       */
391      public static String getEnvironmentArg(String variable) {
392        if (!VM.runningVM) throw new IllegalAccessError("Environment variables can't be read in a non-running VM");
393        String[] allEnvArgs = getArgs(PrefixType.ENVIRONMENT_ARG);
394        String prefix = variable + "=";
395        if (allEnvArgs != null) {
396          for (String allEnvArg : allEnvArgs) {
397            if (allEnvArg.startsWith(prefix)) {
398              return allEnvArg.substring(variable.length() + 1);
399            }
400          }
401        }
402    
403        // There are some that we treat specially.
404        if (variable.equals("java.home")) {
405          return getRvmRoot();
406        } else if (variable.equals("gnu.classpath.home.url")) {
407          return "file:" + getRvmRoot();
408        } else if (variable.equals("gnu.classpath.vm.shortname")) {
409          return "JikesRVM";
410        } else if (variable.equals("user.home")) {
411          return getUserHome();
412        } else if (variable.equals("user.dir")) {
413          return getCWD();
414        } else if (variable.equals("os.name")) {
415          return getOsName();
416        } else if (variable.equals("os.version")) {
417          return getOsVersion();
418        } else if (variable.equals("os.arch")) {
419          return getOsArch();
420        }
421        // Ok, didn't find it.
422        return null;
423      }
424    
425      private static String getRvmRoot() {
426        return null;
427      }
428    
429      private static String getUserHome() {
430        return null;
431      }
432    
433      private static String getCWD() {
434        return null;
435      }
436    
437      private static String getOsName() {
438        return null;
439      }
440    
441      private static String getOsVersion() {
442        return null;
443      }
444    
445      private static String getOsArch() {
446        return null;
447      }
448    
449      /**
450       * Extract the classes that should go through bootstrap classloader.
451       * @return null if no such command line argument is given.
452       */
453      public static String getBootstrapClasses() {
454        String[] vmClassesAll = getArgs(PrefixType.BOOTSTRAP_CLASSES_ARG);
455        String[] prependClasses = getArgs(PrefixType.BOOTCLASSPATH_P_ARG);
456        String[] appendClasses = getArgs(PrefixType.BOOTCLASSPATH_A_ARG);
457    
458        // choose the latest definition of -X:vmClasses
459        String vmClasses = null;
460        // could be specified multiple times, use last specification
461        if (vmClassesAll.length > 0) {
462          vmClasses = vmClassesAll[vmClassesAll.length - 1];
463        }
464    
465        // concatenate all bootclasspath entries
466        String result = vmClasses;
467    
468        for(int c = 0; c < prependClasses.length; c++) {
469          result = prependClasses[c] + ":" + result;
470        }
471    
472        for(int c = 0; c < appendClasses.length; c++) {
473          result = result + ":" + appendClasses[c];
474        }
475    
476        return result;
477      }
478    
479      /**
480       * Stage1 processing of virtual machine directives appearing in argument list.
481       * We try to process as many classes of command line arguments as possible here.
482       * Only those command line arguments that require a more or less
483       * fully booted VM to handle are delayed until lateProcessCommandLineArguments.
484       */
485      static void earlyProcessCommandLineArguments() {
486        for (int i = 0; i < app_name_pos; i++) {
487          String arg = args[i];
488          PrefixType type = arg_types[i];
489          if (type == PrefixType.INVALID_ARG) continue;
490          Prefix p = findPrefix(type);
491          if (DEBUG) VM.sysWriteln(" CommandLineArgs.earlyProcessCLA(" + p + arg + " - " + type + ")");
492          switch (type) {
493    
494            case CLASSPATH_ARG:
495              // arguments of the form "-classpath a:b:c" or "-cp a:b:c"
496              // We are experimentally processing this early so that we can have the
497              // Application class loader complete for when
498              // ClassLoader$StaticData's initializer is run.
499              RVMClassLoader.stashApplicationRepositories(arg);
500              i++; // skip second argument to classpath
501              break;
502    
503            case JAR_ARG:
504              // maybe also load classes on the classpath list in the manifest
505              RVMClassLoader.stashApplicationRepositories(arg);
506              i++; // skip second argument to jar
507              break;
508    
509            case ENABLE_ASSERTION_ARG:
510              // arguments of the form "-ea[:<packagename>...|:<classname>]"
511              RVMClassLoader.stashEnableAssertionArg(arg);
512              break;
513    
514            case ENABLE_SYSTEM_ASSERTION_ARG:
515              // arguments of the form "-esa[:<packagename>...|:<classname>]"
516              // TODO: currently just treat as -ea
517              RVMClassLoader.stashEnableAssertionArg(arg);
518              break;
519    
520            case DISABLE_ASSERTION_ARG:
521              // arguments of the form "-da[:<packagename>...|:<classname>]"
522              RVMClassLoader.stashDisableAssertionArg(arg);
523              break;
524    
525            case DISABLE_SYSTEM_ASSERTION_ARG:
526              // arguments of the form "-dsa[:<packagename>...|:<classname>]"
527              // TODO: currently just treat as -da
528              RVMClassLoader.stashDisableAssertionArg(arg);
529              break;
530    
531            case VERBOSE_CLS_ARG:
532              VM.verboseClassLoading = true;
533              break;
534    
535            case VERBOSE_JNI_ARG:
536              VM.verboseJNI = true;
537              break;
538    
539            case PROCESSORS_ARG: // "-X:processors=<n>" or "-X:processors=all"
540              int nProcs;
541              if (arg.equals("all")) {
542                /* Assume sysCall.sysNumProcessors() always returns a sane
543              value.  */
544                nProcs = sysCall.sysNumProcessors();
545              } else {
546                nProcs = primitiveParseInt(arg);
547              }
548              if (nProcs < 1) {
549                VM.sysWrite("vm: ", p.value, " needs an argument that is at least 1");
550                VM.sysWriteln(", but found ", arg);
551                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
552              }
553              RVMThread.numProcessors = nProcs;
554              break;
555    
556              // -------------------------------------------------------------------
557              // GC options
558              // -------------------------------------------------------------------
559            case GC_HELP_ARG:  // -X:gc passed 'help' as an option
560              MemoryManager.processCommandLineArg("help");
561              break;
562            case GC_ARG: // "-X:gc:arg" pass 'arg' as an option
563              MemoryManager.processCommandLineArg(arg);
564              break;
565    
566              // ----------------------------------------------------
567              // Access initial runtime compiler (may be baseline or optimizing).
568              // ----------------------------------------------------
569            case IRC_HELP_ARG:
570              RuntimeCompiler.processCommandLineArg("-X:irc:", "help");
571              break;
572            case IRC_ARG: // "-X:irc:arg"; pass 'arg' as an option
573              RuntimeCompiler.processCommandLineArg("-X:irc:", arg);
574              break;
575    
576              // --------------------------------------------------------------------
577              // Access adaptive system's recompilation compilers
578              // Currently this means the opt compiler, but in general we could be
579              // talking to several different compilers used by AOS for recompilation.
580              // --------------------------------------------------------------------
581            case RECOMP_HELP_ARG:
582              if (VM.BuildForAdaptiveSystem) {
583                Controller.addOptCompilerOption("opt:help");
584              } else {
585                VM.sysWriteln("vm: nonadaptive configuration; -X:recomp is not a legal prefix in this configuration");
586                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
587              }
588              break;
589            case RECOMP_ARG:
590              // "-X:recomp[?]:arg" process as 'opt[?]:arg' to opt
591              // Note arg actually includes the optional opt level and :
592              if (VM.BuildForAdaptiveSystem) {
593                Controller.addOptCompilerOption("opt" + arg);
594              } else {
595                VM.sysWriteln("vm: nonadaptive configuration; -X:recomp is not a legal prefix in this configuration");
596                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
597              }
598              break;
599    
600              // -------------------------------------------------------------------
601              // Access adaptive optimization system
602              // -------------------------------------------------------------------
603            case AOS_HELP_ARG:  // -X:aos passed 'help' as an option
604              if (VM.BuildForAdaptiveSystem) {
605                Controller.processCommandLineArg("help");
606              } else {
607                VM.sysWrite("vm: nonadaptive configuration; -X:aos is not a legal prefix in this configuration\n");
608                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
609              }
610              break;
611            case AOS_ARG: // "-X:aos:arg" pass 'arg' as an option
612              if (VM.BuildForAdaptiveSystem) {
613                Controller.processCommandLineArg(arg);
614              } else {
615                VM.sysWrite("vm: nonadaptive configuration; -X:aos is not a legal prefix in this configuration\n");
616                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
617              }
618              break;
619    
620              // ----------------------------------------------------
621              // Access baseline compiler
622              // ----------------------------------------------------
623            case BASE_HELP_ARG:
624              BaselineOptions.printHelp("-X:base:");
625              break;
626            case BASE_ARG: // "-X:base:arg"; pass 'arg' as an option
627              BaselineCompiler.processCommandLineArg(p.value, arg);
628              break;
629    
630              // ----------------------------------------------------
631              // Access all 'logical' optimizing compilers
632              // (both irc and recomp compilers)
633              // ----------------------------------------------------
634            case OPT_HELP_ARG:
635              if (VM.BuildForAdaptiveSystem) {
636                RuntimeCompiler.processOptCommandLineArg("-X:opt:", "help");
637              } else {
638                VM.sysWriteln("vm: You are not using a system that includes the optimizing compiler.");
639                VM.sysWriteln("  Illegal command line argument prefix '-X:opt'");
640                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
641              }
642              break;
643            case OPT_ARG: // "-X:opt:arg"; pass 'arg' as an option
644              if (VM.BuildForAdaptiveSystem) {
645                RuntimeCompiler.processOptCommandLineArg("-X:opt:", arg);
646                Controller.addOptCompilerOption("opt:" + arg);
647              } else {
648                VM.sysWriteln("vm: You are not using a system that includes the optimizing compiler.");
649                VM.sysWriteln("  Illegal command line argument prefix '-X:opt'");
650                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
651              }
652              break;
653    
654              // -------------------------------------------------------------------
655              // Other arguments to the core VM
656              // -------------------------------------------------------------------
657            case HELP_ARG:  // -X:vm passed 'help' as an option
658              Options.printHelp();
659              break;
660            case ARG: // "-X:vm:arg" pass 'arg' as an option
661              if (!Options.process(arg)) {
662                VM.sysWriteln("Unrecognized command line argument ", p.value, arg);
663                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
664              }
665              break;
666          }
667        }
668      }
669    
670      /**
671       * Stage2 processing of virtual machine directives appearing in argument list.
672       * This function is responsible for processing the few
673       * command line arguments that need to be handled late in booting.
674       * It also returns the application's command line arguments.
675       *
676       * @return application arguments (first is application class name)
677       * If no application arguments are specified on the command line,
678       * process commands anyway.
679       */
680      static String[] lateProcessCommandLineArguments() {
681        for (int i = 0; i < app_name_pos; i++) {
682          String arg = args[i];
683          PrefixType type = arg_types[i];
684          if (type == PrefixType.INVALID_ARG) continue;
685          Prefix p = findPrefix(type);
686          if (DEBUG) VM.sysWriteln(" CommandLineArgs.processCLA(" + p + arg + " - " + type + ")");
687          switch (type) {
688            case ENVIRONMENT_ARG: // arguments of the form "-Dx=y"
689            {
690              int mid = arg.indexOf('=');
691              if (mid == -1 || mid + 1 == arg.length()) {
692                VM.sysWriteln("vm: bad property setting: \"", arg, "\"");
693                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
694              }
695              String name = arg.substring(0, mid);
696              String value = arg.substring(mid + 1);
697              System.getProperties().put(name, value);
698            }
699            break;
700    
701            case CLASSPATH_ARG:   // This is run in duplicate.
702              // arguments of the form "-classpath a:b:c" or "-cp a:b:c"
703              RVMClassLoader.setApplicationRepositories(arg);
704              i++; // skip second argument to classpath
705              break;
706    
707            case JAR_ARG:             // XXX This WILL BECOME  the second half of
708              // handling JAR_ARG.  TODO
709              // arguments of the form -jar <jarfile>
710              java.util.jar.Manifest mf = null;
711              try {
712                java.util.jar.JarFile jf = new java.util.jar.JarFile(arg);
713                mf = jf.getManifest();
714              } catch (Exception e) {
715                VM.sysWriteln("vm: IO Exception opening JAR file ", arg, ": ", e.getMessage());
716                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
717              }
718              if (mf == null) {
719                VM.sysWriteln("The jar file is missing the manifest entry for the main class: ", arg);
720                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
721              }
722              String s = mf.getMainAttributes().getValue("Main-Class");
723              if (s == null) {
724                VM.sysWriteln("The jar file is missing the manifest entry for the main class: ", arg);
725                VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
726              }
727              // maybe also load classes on the classpath list in the manifest
728              RVMClassLoader.setApplicationRepositories(arg);
729    
730              args[i] = s;
731              arg_types[i] = PrefixType.APPLICATION_ARG;
732              app_prefix.count++;
733              i++; // skip second argument to classpath
734              break;
735            case JAVAAGENT_ARG:
736              /* Extract jar file from the -javaagent:<jar>[=options] form */
737              int equalsPos = arg.indexOf("=");
738              String jarPath;
739              if (equalsPos != -1) {
740                jarPath = arg.substring(0, equalsPos);
741              } else {
742                jarPath = arg;
743              }
744              String newClassPath = RVMClassLoader.getApplicationRepositories() + File.pathSeparator + jarPath;
745              RVMClassLoader.setApplicationRepositories(newClassPath);
746              break;
747          }
748        }
749    
750        // get application directives
751        String[] arglist = getArgs(PrefixType.APPLICATION_ARG);
752    
753        // Debugging: write out application arguments
754        if (DEBUG) {
755          VM.sysWrite("VM.CommandLineArgs(): application arguments " + arglist.length + "\n");
756          for (int i = 0; i < arglist.length; i++) {
757            VM.sysWrite(i + ": \"" + arglist[i] + "\"\n");
758          }
759        }
760    
761        return arglist;
762      }
763    
764      /**
765       * Read the <code>argno</code>'th command line argument from the C argv
766       * @param argno Number of argument sought
767       * @param buf   Buffer to fill
768       * @return number of bytes placed in buffer. -1 means buffer too small
769       *         for argument to fit)
770       */
771      private static int sysArg(int argno, byte[] buf) {
772        return sysCall.sysArg(argno, buf, buf.length);
773      }
774    
775      /**
776       * Primitive parsing of float/double values.
777       * Done this way to enable us to parse command line arguments
778       * early in VM booting before we are able to do the JNI call
779       * that using Double.valueOf would require.
780       * Does not support the full Java spec.
781       */
782      public static float primitiveParseFloat(String arg) {
783        byte[] b = stringToBytes("floating point", arg);
784        return sysCall.sysPrimitiveParseFloat(b);
785      }
786    
787      /**
788       * Primitive parsing of byte/integer numeric values.
789       * Done this way to enable us to parse command line arguments
790       * early in VM booting before we are able call
791       * Byte.parseByte or Integer.parseInt.
792       */
793      public static int primitiveParseInt(String arg) {
794        byte[] b = stringToBytes("integer or byte", arg);
795        return sysCall.sysPrimitiveParseInt(b);
796      }
797    
798      /**
799       * Primitive parsing of memory sizes, with proper error handling,
800       * and so on.
801       * Works without needing Byte.parseByte or Integer.parseInt().
802       *
803       * At the moment, we have a maximum limit of an unsigned integer.  If
804       *
805       * @return Negative values on error.
806       *      Otherwise, positive or zero values as bytes.
807       * */
808      public static long parseMemorySize(String sizeName, String sizeFlag, String defaultFactor, int roundTo,
809                                         String fullArg, String subArg) {
810        return sysCall.sysParseMemorySize(s2b(sizeName),
811                                          s2b(sizeFlag),
812                                          s2b(defaultFactor),
813                                          roundTo,
814                                          s2b(fullArg),
815                                          s2b(subArg));
816      }
817    
818      private static final class ArgReader {
819        //    int buflen = 10;              // for testing; small enough to force
820        // reallocation really soon.
821        int buflen = 512;
822    
823        byte[] buf;                 // gets freed with the class instance.
824    
825        ArgReader() {
826          buf = new byte[buflen];
827        }
828    
829        /** Read argument # @param i
830         * Assume arguments are encoded in the platform's
831         * "default character set". */
832        @SuppressWarnings({"deprecation"})
833        String getArg(int i) {
834          int cnt;
835          for (; ;) {
836            cnt = sysArg(i, buf);
837            if (cnt >= 0) {
838              break;
839            }
840            buflen += 1024;
841            buf = new byte[buflen];
842          }
843          if (VM.VerifyAssertions) VM._assert(cnt != -1);
844          /*
845           * Implementation note: Do NOT use the line below, which uses the
846           * three-argument constructor for String, the one that respects the native
847           * encoding (the platform's "default character set").
848           *
849           * Instead, we use the four-argument constructor, the one that takes a
850           * HIBYTE parameter.
851           *
852           * 1) It is safe to do this; we *know* that all of the legal command-line
853           * args use only characters within the ASCII character set.
854           *
855           * 2) The "default character set" version below will break. That is
856           * because GNU Classpath's implementation of the
857           * three-argument-constructor will fail if EncodingManager.getDecoder()
858           * returns a null pointer. And EncodingManager.getDecoder() returns a null
859           * pointer if it's called early on in the boot process (which the
860           * default-character-set version below does).
861           */
862          //      return new String(buf, 0, cnt);
863          return new String(buf, 0, 0, cnt);
864        }
865    
866        int numArgs() {
867          return sysArg(-1, buf);
868        }
869      }
870    
871      /** Convenience method for calling stringToBytes */
872      private static byte[] s2b(String arg) {
873        return stringToBytes(null, arg);
874      }
875    
876      /** Convert the string s (the "argument") to a null-terminated byte array.
877       * This is used for converting arguments and for converting fixed
878       * strings we pass down to lower commands.
879       *
880       * @return a byte array that represents <code>arg</code> as a
881       * null-terminated C string.
882       * Returns null for a null arg.
883       *
884       * @param arg the argument to convert
885       * @param argName text to print for error reporting. */
886      private static byte[] stringToBytes(String argName, String arg) {
887        if (arg == null) {
888          return null;
889        }
890        int len = arg.length();
891        byte[] b = new byte[len + 1];
892    
893        for (int i = 0; i < len; i++) {
894          char c = arg.charAt(i);
895          if (c > 127) {
896            VM.sysWrite("vm: Invalid character found in a");
897            if (argName == null) {
898              VM.sysWrite("n");
899            } else {
900              char v = argName.charAt(0);
901              switch (v) {
902                case'a':
903                case'e':
904                case'i':
905                case'o':
906                case'u':
907                  VM.sysWrite("n");
908              }
909              VM.sysWrite(" ", argName);
910            }
911            VM.sysWriteln(" argument: >", arg, "<");
912            VM.sysExit(VM.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
913          }
914          b[i] = (byte) c;
915        }
916        return b;
917      }
918    }