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