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