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 }