
com.oracle.svm.driver.APIOptionHandler Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2018, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.oracle.svm.driver;
import java.lang.reflect.Field;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ServiceLoader;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.graalvm.collections.EconomicMap;
import org.graalvm.compiler.options.OptionDescriptor;
import org.graalvm.compiler.options.OptionDescriptors;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.option.APIOption;
import com.oracle.svm.core.option.APIOption.APIOptionKind;
import com.oracle.svm.core.option.APIOptionGroup;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.driver.NativeImage.ArgumentQueue;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.option.HostedOptionParser;
import com.oracle.svm.util.ReflectionUtil;
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;
class APIOptionHandler extends NativeImage.OptionHandler {
static final class OptionInfo {
final String[] variants;
final char valueSeparator;
final String builderOption;
final String defaultValue;
final String helpText;
final boolean hasPathArguments;
final boolean defaultFinal;
final String deprecationWarning;
final boolean extra;
final List> valueTransformers;
final APIOptionGroup group;
OptionInfo(String[] variants, char valueSeparator, String builderOption, String defaultValue, String helpText, boolean hasPathArguments, boolean defaultFinal, String deprecationWarning,
List> valueTransformers, APIOptionGroup group, boolean extra) {
this.variants = variants;
this.valueSeparator = valueSeparator;
this.builderOption = builderOption;
this.defaultValue = defaultValue;
this.helpText = helpText;
this.hasPathArguments = hasPathArguments;
this.defaultFinal = defaultFinal;
this.deprecationWarning = deprecationWarning;
this.valueTransformers = valueTransformers;
this.group = group;
this.extra = extra;
}
boolean isDeprecated() {
return deprecationWarning.length() > 0;
}
}
private final SortedMap apiOptions;
private final Map groupInfos;
APIOptionHandler(NativeImage nativeImage) {
super(nativeImage);
if (NativeImage.IS_AOT) {
APIOptionSupport support = ImageSingletons.lookup(APIOptionSupport.class);
groupInfos = support.groupInfos;
apiOptions = support.options;
} else {
List> optionDescriptorsList = new ArrayList<>();
ServiceLoader serviceLoader = ServiceLoader.load(OptionDescriptors.class, nativeImage.getClass().getClassLoader());
for (OptionDescriptors optionDescriptors : serviceLoader) {
optionDescriptorsList.add(optionDescriptors.getClass());
}
groupInfos = new HashMap<>();
apiOptions = extractOptions(optionDescriptorsList, groupInfos);
}
}
static SortedMap extractOptions(List> optionsClasses, Map groupInfos) {
EconomicMap hostedOptions = EconomicMap.create();
EconomicMap runtimeOptions = EconomicMap.create();
HostedOptionParser.collectOptions(optionsClasses, hostedOptions, runtimeOptions);
SortedMap apiOptions = new TreeMap<>();
Map, APIOptionGroup> groupInstances = new HashMap<>();
hostedOptions.getValues().forEach(o -> extractOption(NativeImage.oH, o, apiOptions, groupInfos, groupInstances));
runtimeOptions.getValues().forEach(o -> extractOption(NativeImage.oR, o, apiOptions, groupInfos, groupInstances));
groupInfos.forEach((groupName, groupInfo) -> {
if (groupInfo.defaultValues.size() > 1) {
VMError.shouldNotReachHere(String.format("APIOptionGroup %s must only have a single default (but has: %s)",
groupName, String.join(", ", groupInfo.defaultValues)));
}
});
return apiOptions;
}
private static void extractOption(String optionPrefix, OptionDescriptor optionDescriptor, SortedMap apiOptions,
Map groupInfos, Map, APIOptionGroup> groupInstances) {
try {
Field optionField = optionDescriptor.getDeclaringClass().getDeclaredField(optionDescriptor.getFieldName());
APIOption[] apiAnnotations = optionField.getAnnotationsByType(APIOption.class);
for (APIOption apiAnnotation : apiAnnotations) {
String builderOption = optionPrefix;
if (apiAnnotation.name().length <= 0) {
VMError.shouldNotReachHere(String.format("APIOption for %s does not provide a name entry", optionDescriptor.getLocation()));
}
String apiOptionName = APIOption.Utils.optionName(apiAnnotation.name()[0]);
String rawOptionName = optionDescriptor.getName();
APIOptionGroup group = null;
String defaultValue = null;
boolean booleanOption = false;
Class> optionValueType = optionDescriptor.getOptionValueType();
if (optionValueType.isArray()) {
VMError.guarantee(optionDescriptor.getOptionKey() instanceof HostedOptionKey, "Only HostedOptionKeys are allowed to have array type key values.");
optionValueType = optionValueType.getComponentType();
}
if (optionValueType.equals(Boolean.class)) {
if (!apiAnnotation.group().equals(APIOption.NullGroup.class)) {
try {
Class extends APIOptionGroup> groupClass = apiAnnotation.group();
APIOptionGroup g = group = groupInstances.computeIfAbsent(groupClass, ReflectionUtil::newInstance);
String groupName = APIOption.Utils.groupName(group);
GroupInfo groupInfo = groupInfos.computeIfAbsent(groupName, (n) -> new GroupInfo(g));
if (group.helpText() == null || group.helpText().isEmpty()) {
VMError.shouldNotReachHere(String.format("APIOptionGroup %s(%s) needs to provide help text", groupClass.getName(), group.name()));
}
String groupMember = apiAnnotation.name()[0];
groupInfo.supportedValues.add(groupMember);
apiOptionName = groupName + groupMember;
Boolean isEnabled = (Boolean) optionDescriptor.getOptionKey().getDefaultValue();
if (isEnabled) {
groupInfo.defaultValues.add(groupMember);
/* Use OptionInfo.defaultValue to remember group default value */
defaultValue = groupMember;
}
} catch (ReflectionUtilError ex) {
throw VMError.shouldNotReachHere(
"Class specified as group for @APIOption " + apiOptionName + " cannot be loaded or instantiated: " + apiAnnotation.group().getTypeName(), ex.getCause());
}
}
if (apiAnnotation.kind().equals(APIOptionKind.Paths)) {
VMError.shouldNotReachHere(String.format("Boolean APIOption %s(%s) cannot use APIOptionKind.Paths", apiOptionName, rawOptionName));
}
if (apiAnnotation.defaultValue().length > 0) {
VMError.shouldNotReachHere(String.format("Boolean APIOption %s(%s) cannot use APIOption.defaultValue", apiOptionName, rawOptionName));
}
if (apiAnnotation.fixedValue().length > 0) {
VMError.shouldNotReachHere(String.format("Boolean APIOption %s(%s) cannot use APIOption.fixedValue", apiOptionName, rawOptionName));
}
builderOption += apiAnnotation.kind().equals(APIOptionKind.Negated) ? "-" : "+";
builderOption += rawOptionName;
booleanOption = true;
} else {
if (!apiAnnotation.group().equals(APIOption.NullGroup.class)) {
VMError.shouldNotReachHere(String.format("Using @APIOption.group not supported for non-boolean APIOption %s(%s)", apiOptionName, rawOptionName));
}
if (apiAnnotation.kind().equals(APIOptionKind.Negated)) {
VMError.shouldNotReachHere(String.format("Non-boolean APIOption %s(%s) cannot use APIOptionKind.Negated", apiOptionName, rawOptionName));
}
if (apiAnnotation.defaultValue().length > 1) {
VMError.shouldNotReachHere(String.format("APIOption %s(%s) cannot have more than one APIOption.defaultValue", apiOptionName, rawOptionName));
}
if (apiAnnotation.fixedValue().length > 1) {
VMError.shouldNotReachHere(String.format("APIOption %s(%s) cannot have more than one APIOption.fixedValue", apiOptionName, rawOptionName));
}
if (apiAnnotation.fixedValue().length > 0 && apiAnnotation.defaultValue().length > 0) {
VMError.shouldNotReachHere(String.format("APIOption %s(%s) APIOption.defaultValue and APIOption.fixedValue cannot be combined", apiOptionName, rawOptionName));
}
if (apiAnnotation.defaultValue().length > 0) {
defaultValue = apiAnnotation.defaultValue()[0];
}
if (apiAnnotation.fixedValue().length > 0) {
defaultValue = apiAnnotation.fixedValue()[0];
}
builderOption += rawOptionName;
builderOption += "=";
}
String helpText = optionDescriptor.getHelp();
if (!apiAnnotation.customHelp().isEmpty()) {
helpText = apiAnnotation.customHelp();
}
if (helpText == null || helpText.isEmpty()) {
VMError.shouldNotReachHere(String.format("APIOption %s(%s) needs to provide help text", apiOptionName, rawOptionName));
}
if (group == null) {
/* Regular help text needs to start with lower-case letter */
helpText = startLowerCase(helpText);
}
List> valueTransformers = new ArrayList<>(apiAnnotation.valueTransformer().length);
for (Class extends Function
© 2015 - 2025 Weber Informatics LLC | Privacy Policy