From c629840fd601f9bf37260f6a7fdfd4c6a4ec76b3 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 7 Nov 2023 11:13:31 +0200 Subject: [PATCH 1/6] Use synchronized data structures for reachability handlers registration Prevent data races in reachability handlers registration when using `-H:-RunReachabilityHandlersConcurrently`. Closes https://github.com/oracle/graal/issues/5868 (cherry picked from commit 777cb827c6ec1a6dff803ad29fa3057c8b646f13) --- .../oracle/svm/hosted/ReachabilityHandlerFeature.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReachabilityHandlerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReachabilityHandlerFeature.java index a1e8c65b810f..e8a04028df35 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReachabilityHandlerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReachabilityHandlerFeature.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -44,6 +45,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.util.ConcurrentIdentityHashMap; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; @@ -52,8 +54,8 @@ @AutomaticallyRegisteredFeature public class ReachabilityHandlerFeature implements InternalFeature, ReachabilityHandler { - private final IdentityHashMap> activeHandlers = new IdentityHashMap<>(); - private final IdentityHashMap>> triggeredHandlers = new IdentityHashMap<>(); + private final Map> activeHandlers = new ConcurrentIdentityHashMap<>(); + private final Map>> triggeredHandlers = new ConcurrentIdentityHashMap<>(); public static ReachabilityHandlerFeature singleton() { return ImageSingletons.lookup(ReachabilityHandlerFeature.class); @@ -93,7 +95,7 @@ private void registerReachabilityHandler(BeforeAnalysisAccess a, Object callback BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a; AnalysisMetaAccess metaAccess = access.getMetaAccess(); - Set triggerSet = activeHandlers.computeIfAbsent(callback, c -> new HashSet<>()); + var triggerSet = activeHandlers.computeIfAbsent(callback, c -> ConcurrentHashMap.newKeySet()); for (Object trigger : triggers) { if (trigger instanceof Class) { @@ -125,7 +127,7 @@ public void duringAnalysis(DuringAnalysisAccess a) { Set triggers = activeHandlers.get(callback); if (callback instanceof Consumer) { if (isTriggered(access, triggers)) { - triggeredHandlers.put(callback, null); + triggeredHandlers.put(callback, Map.of()); toExactCallback(callback).accept(access); completedCallbacks.add(callback); } From 08c1d09a78e7719119376a5b98671e07531ddd00 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 12 Jul 2023 18:31:57 +0300 Subject: [PATCH 2/6] Throw exception for null in RuntimeJNIAccess/RuntimeReflection reg. Don't allow null values to be passed to the `register` method of `RuntimeJNIAccess` and `RuntimeReflection`. Since these are public APIs GraalVM should either handle null values (by ignoring them in this case) or throw a `NullPointerException` before creating an asynchronous task to perform the registration in the analysis, which then results in `NullPointerException`s being thrown later when it's no longer possible to understand where the null value originate from. (cherry picked from commit e6c12dd389609f0eac07aa93d085fa33dd21bce0) --- .../nativeimage/impl/ReflectionRegistry.java | 8 +- substratevm/mx.substratevm/mx_substratevm.py | 4 +- .../ConditionalConfigurationRegistry.java | 8 ++ .../hosted/reflect/ReflectionDataBuilder.java | 8 ++ .../svm/test/ReflectionRegistrationTest.java | 120 ++++++++++++++++++ 5 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/ReflectionRegistrationTest.java diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java index 9ece13ae678f..cc5e501e43b5 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java @@ -46,7 +46,13 @@ public interface ReflectionRegistry { default void register(ConfigurationCondition condition, Class... classes) { - Arrays.stream(classes).forEach(clazz -> register(condition, false, clazz)); + Arrays.stream(classes).forEach(clazz -> { + if (clazz == null) { + throw new NullPointerException("Cannot register null value as class for reflection. " + + "Please ensure that all values you register are not null."); + } + register(condition, false, clazz); + }); } void register(ConfigurationCondition condition, boolean unsafeAllocated, Class clazz); diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 2fc9dad14ff8..ff659d0e128b 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1,7 +1,7 @@ # # ---------------------------------------------------------------------------------------------------- # -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2023, 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 @@ -307,7 +307,7 @@ def native_image_func(args, **kwargs): yield native_image_func native_image_context.hosted_assertions = ['-J-ea', '-J-esa'] -_native_unittest_features = '--features=com.oracle.svm.test.ImageInfoTest$TestFeature,com.oracle.svm.test.ServiceLoaderTest$TestFeature,com.oracle.svm.test.SecurityServiceTest$TestFeature' +_native_unittest_features = '--features=com.oracle.svm.test.ImageInfoTest$TestFeature,com.oracle.svm.test.ServiceLoaderTest$TestFeature,com.oracle.svm.test.SecurityServiceTest$TestFeature,com.oracle.svm.test.ReflectionRegistrationTest$TestFeature' IMAGE_ASSERTION_FLAGS = ['-H:+VerifyGraalGraphs', '-H:+VerifyPhases'] DEVMODE_FLAGS = ['-Ob'] diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java index 1ee6e315c3d2..061d594933a3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java @@ -38,6 +38,14 @@ public abstract class ConditionalConfigurationRegistry { private final Map> pendingReachabilityHandlers = new ConcurrentHashMap<>(); protected void registerConditionalConfiguration(ConfigurationCondition condition, Runnable runnable) { + if (condition == null) { + throw new NullPointerException("Cannot use null value as condition for conditional configuration. " + + "Please ensure that you register a non-null condition."); + } + if (runnable == null) { + throw new NullPointerException("Cannot use null value as runnable for conditional configuration. " + + "Please ensure that you register a non-null runnable."); + } if (ConfigurationCondition.alwaysTrue().equals(condition)) { /* analysis optimization to include new types as early as possible */ runnable.run(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index 1faacb5671e9..f72be32c2942 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -154,6 +154,10 @@ public void register(ConfigurationCondition condition, boolean queriedOnly, Exec private void registerMethods(boolean queriedOnly, Executable[] methods) { for (Executable method : methods) { + if (method == null) { + throw new NullPointerException("Cannot register null value as method for reflection. " + + "Please ensure that all values you register are not null."); + } ExecutableAccessibility oldValue; ExecutableAccessibility newValue; do { @@ -178,6 +182,10 @@ public void register(ConfigurationCondition condition, boolean finalIsWritable, private void registerFields(Field[] fields) { // Unsafe and write accesses are always enabled for fields because accessors use Unsafe. for (Field field : fields) { + if (field == null) { + throw new NullPointerException("Cannot register null value as field for reflection. " + + "Please ensure that all values you register are not null."); + } if (reflectionFields.add(field)) { modifiedClasses.add(field.getDeclaringClass()); } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/ReflectionRegistrationTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/ReflectionRegistrationTest.java new file mode 100644 index 000000000000..555cc683657d --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/ReflectionRegistrationTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, Red Hat Inc. 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.test; + +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.RuntimeReflection; +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import org.junit.Test; + +import java.lang.reflect.Executable; +import java.lang.reflect.Field; + +/** + * Tests the {@link RuntimeReflection}. + */ +public class ReflectionRegistrationTest { + + public static class TestFeature implements Feature { + + @SuppressWarnings("unused")// + int unusedVariableOne = 1; + @SuppressWarnings("unused")// + int unusedVariableTwo = 2; + + @Override + public void beforeAnalysis(final BeforeAnalysisAccess access) { + try { + RuntimeReflection.register((Class) null); + assert false; + } catch (NullPointerException e) { + assert e.getMessage().startsWith("Cannot register null value"); + } + try { + RuntimeReflection.register((Executable) null); + assert false; + } catch (NullPointerException e) { + assert e.getMessage().startsWith("Cannot register null value"); + } + try { + RuntimeReflection.register((Field) null); + assert false; + } catch (NullPointerException e) { + assert e.getMessage().startsWith("Cannot register null value"); + } + + try { + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(null, this.getClass()); + assert false; + } catch (NullPointerException e) { + assert e.getMessage().startsWith("Cannot use null value"); + } + + try { + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(null, true, this.getClass().getMethods()); + assert false; + } catch (NullPointerException e) { + assert e.getMessage().startsWith("Cannot use null value"); + } + + try { + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(null, true, this.getClass().getFields()); + assert false; + } catch (NullPointerException e) { + assert e.getMessage().startsWith("Cannot use null value"); + } + + FeatureImpl.BeforeAnalysisAccessImpl impl = (FeatureImpl.BeforeAnalysisAccessImpl) access; + try { + SubstitutionReflectivityFilter.shouldExclude((Class) null, impl.getMetaAccess(), impl.getUniverse()); + assert false; + } catch (NullPointerException e) { + // expected + } + try { + SubstitutionReflectivityFilter.shouldExclude((Executable) null, impl.getMetaAccess(), impl.getUniverse()); + assert false; + } catch (NullPointerException e) { + // expected + } + try { + SubstitutionReflectivityFilter.shouldExclude((Field) null, impl.getMetaAccess(), impl.getUniverse()); + assert false; + } catch (NullPointerException e) { + // expected + } + } + + } + + @Test + public void test() { + // nothing to do + } +} From 2f90e1a148287e5bf1dc040fa49fb3eb6ef45f51 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Wed, 1 Nov 2023 17:43:25 +0100 Subject: [PATCH 3/6] Fix style. (cherry picked from commit d621dbd5b1e9f3e3095e05b062b39afc4c6f3588) --- .../src/org/graalvm/nativeimage/impl/ReflectionRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java index cc5e501e43b5..56217e4b9839 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java @@ -49,7 +49,7 @@ default void register(ConfigurationCondition condition, Class... classes) { Arrays.stream(classes).forEach(clazz -> { if (clazz == null) { throw new NullPointerException("Cannot register null value as class for reflection. " + - "Please ensure that all values you register are not null."); + "Please ensure that all values you register are not null."); } register(condition, false, clazz); }); From 158239a1dd1ecc63305da3b6efbe82dff9f8cbb0 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Thu, 2 Nov 2023 14:26:29 +0100 Subject: [PATCH 4/6] Move null checks to the beginning of register methods. Not before the register methods, which can miss cases, nor later on in a runnable. (cherry picked from commit f94551abad61ae492d9a0b266a07564ab8aff92e) --- .../nativeimage/impl/ReflectionRegistry.java | 8 +------ .../hosted/reflect/ReflectionDataBuilder.java | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java index 56217e4b9839..9ece13ae678f 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java @@ -46,13 +46,7 @@ public interface ReflectionRegistry { default void register(ConfigurationCondition condition, Class... classes) { - Arrays.stream(classes).forEach(clazz -> { - if (clazz == null) { - throw new NullPointerException("Cannot register null value as class for reflection. " + - "Please ensure that all values you register are not null."); - } - register(condition, false, clazz); - }); + Arrays.stream(classes).forEach(clazz -> register(condition, false, clazz)); } void register(ConfigurationCondition condition, boolean unsafeAllocated, Class clazz); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index f72be32c2942..d9861e5ffca0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -44,6 +44,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; @@ -127,6 +128,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl @Override public void register(ConfigurationCondition condition, boolean unsafeInstantiated, Class clazz) { + Objects.requireNonNull(clazz, () -> nullErrorMessage("class")); checkNotSealed(); registerConditionalConfiguration(condition, () -> { if (unsafeInstantiated) { @@ -148,16 +150,13 @@ public void registerClassLookupException(ConfigurationCondition condition, Strin @Override public void register(ConfigurationCondition condition, boolean queriedOnly, Executable... methods) { + requireNonNull(methods, "methods"); checkNotSealed(); registerConditionalConfiguration(condition, () -> registerMethods(queriedOnly, methods)); } private void registerMethods(boolean queriedOnly, Executable[] methods) { for (Executable method : methods) { - if (method == null) { - throw new NullPointerException("Cannot register null value as method for reflection. " + - "Please ensure that all values you register are not null."); - } ExecutableAccessibility oldValue; ExecutableAccessibility newValue; do { @@ -175,6 +174,7 @@ private void registerMethods(boolean queriedOnly, Executable[] methods) { @Override public void register(ConfigurationCondition condition, boolean finalIsWritable, Field... fields) { + requireNonNull(fields, "field"); checkNotSealed(); registerConditionalConfiguration(condition, () -> registerFields(fields)); } @@ -182,10 +182,6 @@ public void register(ConfigurationCondition condition, boolean finalIsWritable, private void registerFields(Field[] fields) { // Unsafe and write accesses are always enabled for fields because accessors use Unsafe. for (Field field : fields) { - if (field == null) { - throw new NullPointerException("Cannot register null value as field for reflection. " + - "Please ensure that all values you register are not null."); - } if (reflectionFields.add(field)) { modifiedClasses.add(field.getDeclaringClass()); } @@ -933,4 +929,14 @@ static ExecutableAccessibility max(ExecutableAccessibility a, ExecutableAccessib return a == Accessed || b == Accessed ? Accessed : QueriedOnly; } } + + private static void requireNonNull(Object[] values, String kind) { + for (Object value : values) { + Objects.requireNonNull(value, () -> nullErrorMessage(kind)); + } + } + + private static String nullErrorMessage(String kind) { + return "Cannot register null value as " + kind + " for reflection. Please ensure that all values you register are not null."; + } } From cdc404cd8bf0af529e352743c87f661867cadbd8 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Thu, 2 Nov 2023 14:27:00 +0100 Subject: [PATCH 5/6] Apply non-null strategy to `JNIAccessFeature`. (cherry picked from commit d996f323b7c6ce796caccff6ae50bd1c2fa5a81f) --- .../oracle/svm/hosted/jni/JNIAccessFeature.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index 2e1a8a50ba95..8787613218fb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; @@ -201,18 +202,21 @@ private class JNIRuntimeAccessibilitySupportImpl extends ConditionalConfiguratio @Override public void register(ConfigurationCondition condition, boolean unsafeAllocated, Class clazz) { assert !unsafeAllocated : "unsafeAllocated can be only set via Unsafe.allocateInstance, not via JNI."; + Objects.requireNonNull(clazz, () -> nullErrorMessage("class")); abortIfSealed(); registerConditionalConfiguration(condition, () -> newClasses.add(clazz)); } @Override public void register(ConfigurationCondition condition, boolean queriedOnly, Executable... methods) { + requireNonNull(methods, "methods"); abortIfSealed(); registerConditionalConfiguration(condition, () -> newMethods.addAll(Arrays.asList(methods))); } @Override public void register(ConfigurationCondition condition, boolean finalIsWritable, Field... fields) { + requireNonNull(fields, "field"); abortIfSealed(); registerConditionalConfiguration(condition, () -> registerFields(finalIsWritable, fields)); } @@ -612,4 +616,14 @@ private static boolean anyFieldMatches(ResolvedJavaType sub, String name) { return false; } } + + private static void requireNonNull(Object[] values, String kind) { + for (Object value : values) { + Objects.requireNonNull(value, () -> nullErrorMessage(kind)); + } + } + + private static String nullErrorMessage(String kind) { + return "Cannot register null value as " + kind + " for JNI access. Please ensure that all values you register are not null."; + } } From 88fcde3fd45854430ab591e5d28f775f40d9c001 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Thu, 2 Nov 2023 14:35:01 +0100 Subject: [PATCH 6/6] Use `Objects.requireNonNull()` in `ConditionalConfigurationRegistry`. (cherry picked from commit 0ba6cc2c33725a482e190525c6f2bd153ec82b2b) --- .../svm/hosted/ConditionalConfigurationRegistry.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java index 061d594933a3..f386e97be495 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import org.graalvm.nativeimage.hosted.Feature; @@ -38,14 +39,8 @@ public abstract class ConditionalConfigurationRegistry { private final Map> pendingReachabilityHandlers = new ConcurrentHashMap<>(); protected void registerConditionalConfiguration(ConfigurationCondition condition, Runnable runnable) { - if (condition == null) { - throw new NullPointerException("Cannot use null value as condition for conditional configuration. " + - "Please ensure that you register a non-null condition."); - } - if (runnable == null) { - throw new NullPointerException("Cannot use null value as runnable for conditional configuration. " + - "Please ensure that you register a non-null runnable."); - } + Objects.requireNonNull(condition, "Cannot use null value as condition for conditional configuration. Please ensure that you register a non-null condition."); + Objects.requireNonNull(runnable, "Cannot use null value as runnable for conditional configuration. Please ensure that you register a non-null runnable."); if (ConfigurationCondition.alwaysTrue().equals(condition)) { /* analysis optimization to include new types as early as possible */ runnable.run();