/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm; import static com.sun.max.platform.Platform.*; import static com.sun.max.vm.VMConfiguration.*; import java.lang.reflect.*; import java.util.*; import sun.misc.*; import sun.reflect.*; import com.sun.max.*; import com.sun.max.annotate.*; import com.sun.max.lang.*; import com.sun.max.memory.*; import com.sun.max.program.*; import com.sun.max.program.option.*; import com.sun.max.unsafe.*; import com.sun.max.util.*; import com.sun.max.vm.MaxineVM.Phase; import com.sun.max.vm.VMOption.Category; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; /** * VM options handling. */ public class VMOptions { @HOSTED_ONLY private static final Comparator<VMOption> OPTION_COMPARATOR = new Comparator<VMOption>() { @Override public int compare(VMOption o1, VMOption o2) { int categoryOrder = o1.category().ordinal() - o2.category().ordinal(); if (categoryOrder != 0) { return categoryOrder; } int nameOrder = o1.name.compareTo(o2.name); if (nameOrder != 0) { return nameOrder; } return o1.prefix.compareTo(o2.prefix); } }; /** * A VM option of the form {@code "-XX:name=value"} that updates a {@code String} field when * the option is {@linkplain VMOption#parseValue(Pointer) parsed}. */ public static final class StringFieldOption extends VMStringOption { public final Field field; StringFieldOption(String prefix, boolean space, String defaultValue, String help, Field field) { super(prefix, space, defaultValue, help); this.field = field; } @Override public boolean parseValue(Pointer optionValue) { boolean result = super.parseValue(optionValue); if (result) { if (MaxineVM.isHosted()) { setFieldValue(field, getValue()); } else { FieldActor fieldActor = FieldActor.fromJava(field); Reference.fromJava(fieldActor.holder().staticTuple()).writeReference(fieldActor.offset(), Reference.fromJava(getValue())); } return true; } return result; } } /** * A VM option of the form {@code "-XX:name=value"} that updates a {@code float} field when * the option is {@linkplain VMOption#parseValue(Pointer) parsed}. */ public static final class FloatFieldOption extends VMFloatOption { public final Field field; FloatFieldOption(String prefix, float defaultValue, String help, Field field) { super(prefix, defaultValue, help); this.field = field; } @Override public boolean parseValue(Pointer optionValue) { boolean result = super.parseValue(optionValue); if (result) { if (MaxineVM.isHosted()) { setFieldValue(field, getValue()); } else { FieldActor fieldActor = FieldActor.fromJava(field); Reference.fromJava(fieldActor.holder().staticTuple()).writeFloat(fieldActor.offset(), getValue()); } return true; } return result; } } /** * A VM option of the form {@code "-XX:name=value"} that updates an {@code int} field when * the option is {@linkplain VMOption#parseValue(Pointer) parsed}. */ public static final class IntFieldOption extends VMIntOption { public final Field field; IntFieldOption(String prefix, int defaultValue, String help, Field field) { super(prefix, defaultValue, help); this.field = field; } @Override public boolean parseValue(Pointer optionValue) { boolean result = super.parseValue(optionValue); if (result) { if (MaxineVM.isHosted()) { setFieldValue(field, getValue()); } else { FieldActor fieldActor = FieldActor.fromJava(field); Reference.fromJava(fieldActor.holder().staticTuple()).writeInt(fieldActor.offset(), getValue()); } return true; } return result; } } /** * A VM option of the form {@code "-XX:name=value"} that updates a {@code Size} field when * the option is {@linkplain VMOption#parseValue(Pointer) parsed}. */ public static final class SizeFieldOption extends VMSizeOption { public final Field field; SizeFieldOption(String prefix, Size defaultValue, String help, Field field) { super(prefix, defaultValue, help); this.field = field; } @Override public boolean parseValue(Pointer optionValue) { boolean result = super.parseValue(optionValue); if (result) { if (MaxineVM.isHosted()) { hostedSetFieldValue(); } else { FieldActor fieldActor = FieldActor.fromJava(field); Reference.fromJava(fieldActor.holder().staticTuple()).writeWord(fieldActor.offset(), getValue()); } return true; } return result; } /** * Factored out as a {@link HOSTED_ONLY} method because it won't verify - it * mixes word and reference types. */ @HOSTED_ONLY void hostedSetFieldValue() { setFieldValue(field, getValue()); } } /** * A VM option of the form {@code "-XX:[+|-]name=value"} that updates a {@code boolean} field when * the option is {@linkplain VMOption#parseValue(Pointer) parsed}. */ public static final class BooleanFieldOption extends VMBooleanXXOption { public final Field field; BooleanFieldOption(String prefix, String name, String help, Field field) { super(prefix, name, help); this.field = field; } @Override public boolean parseValue(Pointer optionValue) { boolean result = super.parseValue(optionValue); if (result) { if (MaxineVM.isHosted()) { setFieldValue(field, getValue()); } else { FieldActor fieldActor = FieldActor.fromJava(field); Reference.fromJava(fieldActor.holder().staticTuple()).writeBoolean(fieldActor.offset(), getValue()); } return true; } return false; } } /** * A VM option of the form {@code "-name"} or {@code "-Xname"} that updates a {@code boolean} field when * the option is {@linkplain VMOption#parseValue(Pointer) parsed}. */ public static final class SimpleBooleanFieldOption extends VMOption { public final Field field; SimpleBooleanFieldOption(String prefix, String help, Field field) { super(prefix + " ", help); this.field = field; } @Override public boolean parseValue(Pointer optionValue) { boolean result = super.parseValue(optionValue); if (result) { if (MaxineVM.isHosted()) { setFieldValue(field, isPresent()); } else { FieldActor fieldActor = FieldActor.fromJava(field); Reference.fromJava(fieldActor.holder().staticTuple()).writeBoolean(fieldActor.offset(), isPresent()); } return true; } return false; } } /** * The set of {@linkplain Phase#PRISTINE pristine-phase} VM options built into the boot image. */ private static VMOption[] pristinePhaseOptions = {}; /** * The set of {@linkplain Phase#STARTING starting-phase} VM options built into the boot image. */ private static VMOption[] startingPhaseOptions = {}; /** * All the options sorted by {@linkplain VMOption#category() category} and then by {@linkplain VMOption#prefix prefix}. */ private static VMOption[] sortedOptions; private static Pointer savedArgv; private static Pointer argv; private static int argc; private static int argumentStart; private static boolean earlyVMExitRequested; private static String[] mainClassArguments; private static String mainClassName; /** * This is a reference to the initial value of {@link System#props} when the VM starts up. * The "magic" in {@link JavaPrototype#hostToTarget(Object)} will ensure that this map * only has the properties from the host specified by {@link JDKInterceptor#REMEMBERED_PROPERTY_NAMES}. * The system properties parsed on the command line are stored in this map. * This is required so that they are available before the System class is initialized. */ private static final Properties initialSystemProperties = System.getProperties(); /** * The {@code -jar} option. */ private static final VMStringOption jarOption = register(new VMStringOption("-jar", true, null, "Executes main class from jar file.") { @Override public boolean isLastOption() { return true; } }, MaxineVM.Phase.PRISTINE); /** * This option is parsed in the native code (see maxine.c). It's declared here simply so that it * shows up in the {@linkplain #printUsage(Category) usage} message. */ private static final VMStringOption logFileOption = register(new VMStringOption("-XX:LogFile=", false, null, "Redirect VM log output to the specified file. By default, VM log output goes to the standard output stream."), MaxineVM.Phase.STARTING); /** * The '-verbose' option and all its variants (e.g. '-verbose:gc', '-verbose:class' etc). */ public static final VerboseVMOption verboseOption = register(new VerboseVMOption(), MaxineVM.Phase.PRISTINE); static { register(new VMIntOption("-XX:TraceLevel=", 0, "Enable tracing output at the specified level.") { @Override public boolean parseValue(Pointer optionValue) { super.parseValue(optionValue); Trace.on(getValue()); return true; } }, MaxineVM.Phase.PRISTINE); register(new VMOption("-X ", "Print help on non-standard options") { @Override public boolean parseValue(Pointer optionValue) { printUsage(Category.NON_STANDARD); return true; } @Override protected boolean haltsVM() { return true; } @Override public Category category() { return Category.STANDARD; } }, MaxineVM.Phase.PRISTINE); register(new VMOption("-XX ", "Print help on Maxine options") { @Override public boolean parseValue(Pointer optionValue) { printUsage(Category.IMPLEMENTATION_SPECIFIC); return true; } @Override protected boolean haltsVM() { return true; } @Override public Category category() { return Category.NON_STANDARD; } }, MaxineVM.Phase.PRISTINE); register(new VMBooleanXXOption("-XX:-PrintConfiguration", "Show VM configuration details and exit") { @Override public boolean parseValue(Pointer optionValue) { Log.println(" Platform: " + platform()); vmConfig().print(Log.out, " "); return true; } @Override protected boolean haltsVM() { return true; } }, MaxineVM.Phase.STARTING); register(new VMBooleanXXOption("-XX:-ShowConfiguration", "Show VM configuration details and continue") { @Override public boolean parseValue(Pointer optionValue) { Log.println(" Platform: " + platform()); vmConfig().print(Log.out, " "); return true; } }, MaxineVM.Phase.STARTING); register(new VMOption("-client", "ignored (present for compatibility)"), MaxineVM.Phase.STARTING); register(new VMOption("-server", "ignored (present for compatibility)"), MaxineVM.Phase.STARTING); } protected VMOptions() { } /** * Gets all the registered VM options as a set sorted by {@linkplain VMOption#category() category} and then by {@linkplain VMOption#prefix prefix}. */ @HOSTED_ONLY public static SortedSet<VMOption> allOptions() { TreeSet<VMOption> result = new TreeSet<VMOption>(OPTION_COMPARATOR); result.addAll(Arrays.asList(pristinePhaseOptions)); result.addAll(Arrays.asList(startingPhaseOptions)); return result; } @HOSTED_ONLY private static VMOption[] addOption(VMOption[] options, VMOption option, Set<VMOption> allOptions) { if (option.category() == VMOption.Category.IMPLEMENTATION_SPECIFIC) { int colon = option.prefix.indexOf(':'); assert colon != -1; final int prefixLength = option instanceof VMBooleanXXOption ? colon + 2 : colon + 1; final String name = option.prefix.substring(prefixLength); ProgramError.check(Character.isUpperCase(name.charAt(0)), "Option with \"-XX:\" prefix must start with an upper-case letter: " + option); } for (VMOption existingOption : allOptions) { ProgramError.check(!existingOption.prefix.equals(option.prefix), "VM option prefix is not unique: " + option.prefix); ProgramError.check(OPTION_COMPARATOR.compare(existingOption, option) != 0, "VM option has non-unique sort key: " + option + " [clashes with " + existingOption + "]"); } return Utils.concat(options, option); } /** * Registers a given VM option in the global option registry that is used to match command * line arguments passed to the VM at runtime. * * @param option a VM option * @param phase the VM phase during which the option should be parsed * @return the {@code option} object */ @HOSTED_ONLY public static <T extends VMOption> T register(VMOption option, MaxineVM.Phase phase) { assert phase != null; final SortedSet<VMOption> allOptions = allOptions(); if (phase == MaxineVM.Phase.PRISTINE) { pristinePhaseOptions = addOption(pristinePhaseOptions, option, allOptions); } else if (phase == MaxineVM.Phase.STARTING) { assert !option.consumesNext(); startingPhaseOptions = addOption(startingPhaseOptions, option, allOptions); } else { throw ProgramError.unexpected("VM options for the " + phase + " phase not (yet) supported"); } allOptions.add(option); sortedOptions = allOptions.toArray(new VMOption[allOptions.size()]); option.findMatchingArgumentAndParse(); final Class<T> type = null; return Utils.cast(type, option); } @HOSTED_ONLY private static final Set<Class> fieldOptionsAdded = new HashSet<Class>(); /** * Creates and registers "-XX" VM options for each non-{@code final} {@code static} field * in a given class. * * @param prefix * @param javaClass the java class containing the fields for which VM options are to be created * @param helpMap map from option names to the help message for the option (may be {@code null}) */ @HOSTED_ONLY public static void addFieldOptions(String prefix, Class<?> javaClass, Map<String, String> helpMap) { if (fieldOptionsAdded.contains(javaClass)) { return; } fieldOptionsAdded.add(javaClass); for (final Field field : javaClass.getDeclaredFields()) { int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) { final OptionSettings settings = field.getAnnotation(OptionSettings.class); String help; String name; if (settings != null) { help = settings.help(); name = settings.name().isEmpty() ? field.getName().replace('_', '-') : settings.name(); } else { assert !field.getName().contains("_"); name = field.getName().replace('_', '-'); help = helpMap != null ? helpMap.get(name) : null; } try { addFieldOption(prefix, name, field, help, MaxineVM.Phase.STARTING); } catch (Exception e) { throw ProgramError.unexpected("Error creating VM option for " + field, e); } } } } @HOSTED_ONLY static void setFieldValue(Field field, Object value) { try { field.set(null, value); } catch (Exception e) { throw ProgramError.unexpected("Error setting value of " + field + " to " + value, e); } } /** * Creates and registers a VM option whose value is stored in a given non-final {@code static} field. * * @param prefix the prefix to use for the option (e.g. {@code "-XX:"} or {@code "-C1X:"}) * @param name the name of the option * @param help the help text for the option */ @HOSTED_ONLY public static VMOption addFieldOption(String prefix, String name, String help) { Class declaringClass = Reflection.getCallerClass(2); return addFieldOption(prefix, name, Classes.getDeclaredField(declaringClass, name), help, MaxineVM.Phase.STARTING); } /** * Creates and registers a VM option whose value is stored in a given non-final {@code static} field. * * @param prefix the prefix to use for the option (e.g. {@code "-XX:"} or {@code "-C1X:"}) * @param name the name of the option * @param declaringClass the class in which a field named {@code name} backing the option * @param help the help text for the option */ @HOSTED_ONLY public static VMOption addFieldOption(String prefix, String name, Class declaringClass, String help) { return addFieldOption(prefix, name, Classes.getDeclaredField(declaringClass, name), help, MaxineVM.Phase.STARTING); } /** * Creates and registers a VM option whose value is stored in a given non-final {@code static} field. * * @param prefix the prefix to use for the option (e.g. {@code "-XX:"} or {@code "-C1X:"}) * @param name the name of the option * @param declaringClass the class in which a field named {@code name} backing the option * @param help the help text for the option * @param phase the VM phase during which the option should be parsed */ @HOSTED_ONLY public static VMOption addFieldOption(String prefix, String name, Class declaringClass, String help, MaxineVM.Phase phase) { return addFieldOption(prefix, name, Classes.getDeclaredField(declaringClass, name), help, phase); } /** * Creates and registers a VM option whose value is stored in a given non-final {@code static} field. * * @param prefix the prefix to use for the option (e.g. {@code "-XX:"} or {@code "-C1X:"}) * @param name the name of the option * @param field the field backing the option * @param help the help text for the option * @param phase the VM phase during which the option should be parsed */ @HOSTED_ONLY public static VMOption addFieldOption(String prefix, String name, Field field, String help, Phase phase) { try { assert Modifier.isStatic(field.getModifiers()); assert !Modifier.isFinal(field.getModifiers()); final Class<?> fieldType = field.getType(); if (MaxineVM.isHosted()) { field.setAccessible(true); } VMOption option; if (fieldType == boolean.class) { boolean defaultValue = field.getBoolean(null); Category c = Category.from(prefix); switch (c) { case STANDARD: case NON_STANDARD: option = new SimpleBooleanFieldOption(prefix + name, help, field); break; case IMPLEMENTATION_SPECIFIC: option = new BooleanFieldOption(prefix + (defaultValue ? '+' : '-'), name, help, field); break; default: throw FatalError.unexpected(c.toString()); } register(option, phase); } else if (fieldType == int.class) { int defaultValue = field.getInt(null); option = new IntFieldOption(prefix + name + "=", defaultValue, help, field); register(option, phase); } else if (fieldType == float.class) { float defaultValue = field.getFloat(null); option = new FloatFieldOption(prefix + name + "=", defaultValue, help, field); register(option, phase); } else if (fieldType == Size.class) { Size defaultValue = (Size) field.get(null); option = new SizeFieldOption(prefix + name + "=", defaultValue, help, field); register(option, phase); } else if (fieldType == String.class) { String defaultValue = (String) field.get(null); option = new StringFieldOption(prefix + name + "=", false, defaultValue, help, field); register(option, phase); } else { throw new RuntimeException("Field type unsupported by VM options"); } return option; } catch (Exception e) { throw ProgramError.unexpected("Error creating VM option for " + field, e); } } public static void printHelpForOption(Category category, String prefix, String value, String help) { Log.print(" "); Log.print(prefix); Log.print(value); if (help != null) { Log.print(" "); int column = 5 + prefix.length() + value.length(); if (column >= category.helpIndent) { Log.println(); column = 0; } for (; column < category.helpIndent; column++) { Log.print(' '); } // reformat the help text by wrapping the lines after column 72. // Strings.formatParagraphs() can't be used because allocation may not work here for (int j = 0; j < help.length(); j++) { final char ch = help.charAt(j); if (column > category.helpLineMaxWidth && (ch == ' ' || ch == '\t')) { Log.println(); for (int k = 0; k < category.helpIndent; k++) { Log.print(' '); } column = category.helpIndent; } else { Log.print(ch); column++; } } } Log.println(); } private static void printOptions(Category category) { for (VMOption option : sortedOptions) { if (option.category() == category) { option.printHelp(); } } } /** * Prints the usage message for a given category or options. * * @param category the category of options to print the usage message for */ public static void printUsage(Category category) { if (category == Category.STANDARD) { Log.println("Usage: maxvm [-options] [class | -jar jarfile] [args...]"); Log.println("where options include:"); printOptions(category); } if (category == Category.NON_STANDARD) { Log.println("Non-standard options:"); printOptions(category); } if (category == Category.IMPLEMENTATION_SPECIFIC) { Log.println("Maxine options:"); printOptions(category); } } /** * Determines if the VM should terminate. This will be true if there was an error while parsing the VM options. * It may also be true if the semantics of some VM option is to print some diagnostic info and then * exit the VM. */ public static boolean earlyVMExitRequested() { return earlyVMExitRequested; } protected static void error(String errorMessage) { earlyVMExitRequested = true; Log.print("VM program argument parsing error: "); Log.println(errorMessage); printUsage(Category.STANDARD); MaxineVM.setExitCode(1); } protected static void error(VMOption option) { earlyVMExitRequested = true; Log.print("Error while parsing "); Log.print(option.toString()); Log.print(": "); option.printErrorMessage(); Log.println(); printUsage(Category.STANDARD); MaxineVM.setExitCode(1); } public static String jarFile() { return jarOption.getValue(); } /** * Gets the index of the next non-empty {@linkplain #argv command line argument} starting at a given index. * * @param start the index of the first argument to consider * @return the index of the first word in {@link #argv} that points to a non-empty C string or -1 if there is no * such command line argument at whose index is greater than or equal to {@code index} and less than * {@link #argc} */ private static int findArgument(int start) { if (start == -1) { return -1; } for (int i = start; i < argc; i++) { final Pointer argument = argv.getWord(0, i).asPointer(); if (!argument.isZero() && !CString.length(argument).isZero()) { return i; } } return -1; } /** * Parses a given option whose {@linkplain VMOption#prefix prefix} is at a given index in the command line * arguments. * * @param index the index of {@code option}'s prefix in the command line arguments * @param argument the command line argument at {@code index} * @param option an option whose prefix matches the beginning of {@code argument} * @return the index of the next command line argument not consumed by {@code option} or -1 if {@code option} * {@linkplain VMOption#isLastOption() terminates} the list of options */ private static int parseOption(int index, final Pointer argument, final VMOption option) { final int nextIndex; if (option.consumesNext()) { // this option expects a space and then its value (e.g. -classpath) if (findArgument(index + 1) != index + 1) { error(option.toString()); } // parse the next argument as this option's value if (!option.parseValue(argv.getWord(index + 1).asPointer())) { error(option.toString()); } else if (option.haltsVM()) { earlyVMExitRequested = true; } argv.setWord(index, Word.zero()); argv.setWord(index + 1, Word.zero()); nextIndex = index + 2; } else { // otherwise ask the option to parse itself if (!option.parse(argument)) { error(option.toString()); } else if (option.haltsVM()) { earlyVMExitRequested = true; } argv.setWord(index, Word.zero()); nextIndex = index + 1; } if (option.isLastOption()) { argumentStart = nextIndex; return -1; } return nextIndex; } private static VMOption findVMOption(Pointer arg, VMOption[] options) { for (VMOption option : options) { if (option.matches(arg)) { return option; } } return null; } /** * Copy the initial argv array. * Currently, argument parsing is destructive and we may access to the original VM arguments * for the runtime management interface (@see getVmArguments) * @param initialArgc * @param initialArgv */ private static Pointer copy(int initialArgc, Pointer initialArgv) { final Size copySize = Size.fromInt(Pointer.size() * initialArgc); Pointer p = Memory.allocate(copySize); Memory.copyBytes(initialArgv, p, copySize); return p; } public static boolean parsePristine(int initialArgc, Pointer initialArgv) { savedArgv = initialArgv; argv = copy(initialArgc, initialArgv); argc = initialArgc; argumentStart = initialArgc; int index = findArgument(1); // skip the first argument (the name of the executable) while (index >= 0) { final Pointer argument = argv.getWord(index).asPointer(); final VMOption option = findVMOption(argument, pristinePhaseOptions); if (option != null) { // some option prefix matched. attempt to parse it. index = parseOption(index, argument, option); if (index < 0) { break; } } else if (argument.getByte() == '-') { index++; // an option to be handled later } else { // the first non-option argument must be the main class, unless -jar argumentStart = index; break; } index = findArgument(index); } return checkOptionsForErrors(pristinePhaseOptions); } protected static int getArgumentCount() { return argumentStart; } protected static Pointer getArgumentCString(int index) { if (index < argumentStart) { return argv.getWord(index).asPointer(); } else { return Pointer.zero(); } } private static String getArgumentString(int index) throws Utf8Exception { final Pointer cArgument = argv.getWord(index).asPointer(); if (cArgument.isZero()) { return null; } final String result = CString.utf8ToJava(cArgument); if (result.isEmpty()) { return null; } return result; } private static boolean checkOptionsForErrors(VMOption[] options) { if (earlyVMExitRequested) { return false; } for (VMOption option : options) { if (!option.check()) { error(option); return false; } } return true; } /** * Support for java.lang.management.RuntimeMXBean. * * @return space-separated string of VM arguments, not including class name, -jar, or user arguments */ public static String getVmArguments() { final StringBuilder sb = new StringBuilder(); int index = 1; boolean needSpace = false; final Pointer p = argv; argv = savedArgv; while (index < argumentStart) { String argument = null; try { argument = getArgumentString(index); } catch (Utf8Exception ex) { } if (argument != null) { if (argument.equals("-jar")) { // skip this and jarfile index++; } else { if (needSpace) { sb.append(' '); } sb.append(argument); needSpace = true; } } index++; } argv = p; return sb.toString(); } /** * Support for {@link VMSupport#initAgentProperties}. * @return space separated string of main class and arguments. */ public static String mainClassAndArguments() { final StringBuilder sb = new StringBuilder(jarFile() == null ? mainClassName : jarFile()); for (int i = 0; i < mainClassArguments.length; i++) { sb.append(' '); sb.append(mainClassArguments[i]); } return sb.toString(); } public static boolean parseStarting() { try { int index = 1; while (index < argumentStart) { final String argument = getArgumentString(index); if (argument == null) { index++; } else { if (argument.startsWith("-D")) { parseSystemProperty(initialSystemProperties, argument); argv.setWord(index, Word.zero()); } else { final Pointer nextArg = argv.getWord(index).asPointer(); final VMOption option = findVMOption(nextArg, startingPhaseOptions); if (option == null) { error("unknown VM argument \"" + CString.utf8ToJava(nextArg) + "\""); } else if (!option.parse(nextArg)) { error("parsing of " + argument + " failed"); } else if (option.haltsVM()) { earlyVMExitRequested = true; } argv.setWord(index, Word.zero()); index++; } } } } catch (Utf8Exception utf8Exception) { error("UTF8 problem"); } return checkOptionsForErrors(startingPhaseOptions); } /** * Adds any system properties specified on the command line to a given properties object. * The command line properties override any properties in {@code properties} that have * the same name. * * @param properties the object to which the command line properties are added */ public static void addParsedSystemProperties(Properties properties) { for (Map.Entry<Object, Object> entry : initialSystemProperties.entrySet()) { properties.setProperty((String) entry.getKey(), (String) entry.getValue()); } } /** * Parses a system property from command line argument that starts with "-D" and adds it * to a given properties object. * * @param properties the object to which the command line property extracted from {@code argument} is added * @param argument a command line argument that starts with "-D" */ private static void parseSystemProperty(Properties properties, final String argument) { final int index = argument.indexOf('='); String name; String value = ""; if (index < 0) { name = argument.substring(2); // chop off -D } else { name = argument.substring(2, index); // get the name of the option value = argument.substring(index + 1); } VMProperty vmProperty = VMProperty.isVMProperty(name); if (vmProperty != null) { if (vmProperty.mutable) { vmProperty.setValue(value); // gets copied into system properties later } } else { properties.setProperty(name, value); } } public static String mainClassName() { return mainClassName; } public static String[] mainClassArguments() { return mainClassArguments; } private static void parseMainClassArguments(int argStart) throws Utf8Exception { int argumentCount = 0; for (int i = argStart; i < argc; i++) { if (findArgument(i) >= 0) { argumentCount++; } } mainClassArguments = new String[argumentCount]; int mainClassArgumentsIndex = 0; for (int i = argStart; i < argc; i++) { if (findArgument(i) >= 0) { mainClassArguments[mainClassArgumentsIndex++] = getArgumentString(i); } } } /** * Tries to parse the next available command line argument which specifies the name of the class containing the main * method to be run. * * @param errorIfNotPresent specifies whether the omission of a main class argument is to be considered an * {@linkplain #error(String) error} * @return true if the main class name argument was successfully parsed. If so, then it's now available by calling * {@link #mainClassName()}. */ public static boolean parseMain(boolean errorIfNotPresent) { try { if (jarOption.isPresent()) { // the first argument is the first argument to the program parseMainClassArguments(argumentStart); return true; } if (argumentStart < argc) { // the first argument is the name of the main class mainClassName = getArgumentString(argumentStart); parseMainClassArguments(argumentStart + 1); return mainClassName != null; } if (errorIfNotPresent) { error("no main class specified"); } } catch (Utf8Exception utf8Exception) { error("UTF8 problem"); } return false; } /** * Calls the {@link VMOption#beforeExit()} method of each registered VM option. */ public static void beforeExit() { for (VMOption option : pristinePhaseOptions) { option.beforeExit(); } for (VMOption option : startingPhaseOptions) { option.beforeExit(); } if (MaxineVM.isHosted()) { for (String argument : VMOption.unmatchedVMArguments()) { if (argument != null) { ProgramWarning.message("VM argument not matched by any VM option: " + argument); } } } } }