Path: blob/master/test/jdk/java/foreign/TestNulls.java
41145 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"26* @modules java.base/jdk.internal.ref27* jdk.incubator.foreign28* @run testng/othervm29* --enable-native-access=ALL-UNNAMED30* TestNulls31*/3233import jdk.incubator.foreign.*;34import jdk.internal.ref.CleanerFactory;35import org.testng.annotations.DataProvider;36import org.testng.annotations.NoInjection;37import org.testng.annotations.Test;3839import java.lang.constant.Constable;40import java.lang.invoke.MethodHandle;41import java.lang.invoke.MethodHandles;42import java.lang.invoke.MethodType;43import java.lang.invoke.VarHandle;44import java.lang.ref.Cleaner;45import java.lang.reflect.Array;46import java.lang.reflect.InvocationTargetException;47import java.lang.reflect.Method;48import java.lang.reflect.Modifier;49import java.nio.ByteBuffer;50import java.nio.ByteOrder;51import java.nio.channels.FileChannel;52import java.nio.charset.Charset;53import java.nio.file.Path;54import java.util.*;55import java.util.concurrent.atomic.AtomicReference;56import java.util.function.Consumer;57import java.util.function.Supplier;58import java.util.function.UnaryOperator;59import java.util.stream.Collectors;60import java.util.stream.Stream;6162import static org.testng.Assert.*;63import static org.testng.Assert.fail;6465/**66* This test makes sure that public API classes (listed in {@link TestNulls#CLASSES}) throws NPEs whenever67* nulls are provided. The test looks at all the public methods in all the listed classes, and injects68* values automatically. If an API takes a reference, the test will try to inject nulls. For APIs taking69* either reference arrays, or collections, the framework will also generate additional <em>replacements</em>70* (e.g. other than just replacing the array, or collection with null), such as an array or collection71* with null elements. The test can be customized by adding/removing classes to the {@link #CLASSES} array,72* by adding/removing default mappings for standard carrier types (see {@link #DEFAULT_VALUES} or by73* adding/removing custom replacements (see {@link #REPLACEMENT_VALUES}).74*/75public class TestNulls {7677static final Class<?>[] CLASSES = new Class<?>[] {78MemorySegment.class,79MemoryAddress.class,80MemoryLayout.class,81MemoryLayout.PathElement.class,82SequenceLayout.class,83ValueLayout.class,84GroupLayout.class,85Addressable.class,86SymbolLookup.class,87MemoryAccess.class,88MemoryLayouts.class,89MemoryHandles.class,90CLinker.class,91CLinker.VaList.class,92CLinker.VaList.Builder.class,93FunctionDescriptor.class,94SegmentAllocator.class,95ResourceScope.class96};9798static final Set<String> EXCLUDE_LIST = Set.of(99"jdk.incubator.foreign.MemoryLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0",100"jdk.incubator.foreign.MemoryAddress/asSegment(long,java.lang.Runnable,java.lang.Object)/1/0",101"jdk.incubator.foreign.MemoryAddress/asSegment(long,java.lang.Runnable,java.lang.Object)/2/0",102"jdk.incubator.foreign.MemoryAddress/asSegment(long,java.lang.Runnable,jdk.incubator.foreign.ResourceScope)/1/0",103"jdk.incubator.foreign.SequenceLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0",104"jdk.incubator.foreign.ValueLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0",105"jdk.incubator.foreign.GroupLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0",106"jdk.incubator.foreign.MemoryHandles/insertCoordinates(java.lang.invoke.VarHandle,int,java.lang.Object[])/2/1",107"jdk.incubator.foreign.FunctionDescriptor/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0"108);109110static final Set<String> OBJECT_METHODS = Stream.of(Object.class.getMethods())111.map(Method::getName)112.collect(Collectors.toSet());113114static final Map<Class<?>, Object> DEFAULT_VALUES = new HashMap<>();115116static <Z> void addDefaultMapping(Class<Z> carrier, Z value) {117DEFAULT_VALUES.put(carrier, value);118}119120static {121addDefaultMapping(char.class, (char)0);122addDefaultMapping(byte.class, (byte)0);123addDefaultMapping(short.class, (short)0);124addDefaultMapping(int.class, 0);125addDefaultMapping(float.class, 0f);126addDefaultMapping(long.class, 0L);127addDefaultMapping(double.class, 0d);128addDefaultMapping(boolean.class, true);129addDefaultMapping(ByteOrder.class, ByteOrder.nativeOrder());130addDefaultMapping(Thread.class, Thread.currentThread());131addDefaultMapping(Cleaner.class, CleanerFactory.cleaner());132addDefaultMapping(ByteBuffer.class, ByteBuffer.wrap(new byte[10]));133addDefaultMapping(Path.class, Path.of("nonExistent"));134addDefaultMapping(FileChannel.MapMode.class, FileChannel.MapMode.PRIVATE);135addDefaultMapping(UnaryOperator.class, UnaryOperator.identity());136addDefaultMapping(String.class, "Hello!");137addDefaultMapping(Constable.class, "Hello!");138addDefaultMapping(Class.class, String.class);139addDefaultMapping(Runnable.class, () -> {});140addDefaultMapping(Object.class, new Object());141addDefaultMapping(VarHandle.class, MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()));142addDefaultMapping(MethodHandle.class, MethodHandles.identity(int.class));143addDefaultMapping(List.class, List.of());144addDefaultMapping(Charset.class, Charset.defaultCharset());145addDefaultMapping(Consumer.class, x -> {});146addDefaultMapping(MethodType.class, MethodType.methodType(void.class));147addDefaultMapping(MemoryAddress.class, MemoryAddress.ofLong(1));148addDefaultMapping(Addressable.class, MemoryAddress.ofLong(1));149addDefaultMapping(MemoryLayout.class, MemoryLayouts.JAVA_INT);150addDefaultMapping(ValueLayout.class, MemoryLayouts.JAVA_INT);151addDefaultMapping(GroupLayout.class, MemoryLayout.structLayout(MemoryLayouts.JAVA_INT));152addDefaultMapping(SequenceLayout.class, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT));153addDefaultMapping(MemorySegment.class, MemorySegment.ofArray(new byte[10]));154addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid());155addDefaultMapping(CLinker.class, CLinker.getInstance());156addDefaultMapping(CLinker.VaList.class, VaListHelper.vaList);157addDefaultMapping(CLinker.VaList.Builder.class, VaListHelper.vaListBuilder);158addDefaultMapping(ResourceScope.class, ResourceScope.newImplicitScope());159addDefaultMapping(SegmentAllocator.class, (size, align) -> null);160addDefaultMapping(Supplier.class, () -> null);161addDefaultMapping(ResourceScope.Handle.class, ResourceScope.globalScope().acquire());162addDefaultMapping(ClassLoader.class, TestNulls.class.getClassLoader());163addDefaultMapping(SymbolLookup.class, CLinker.systemLookup());164}165166static class VaListHelper {167static final CLinker.VaList vaList;168static final CLinker.VaList.Builder vaListBuilder;169170static {171AtomicReference<CLinker.VaList.Builder> builderRef = new AtomicReference<>();172vaList = CLinker.VaList.make(b -> {173builderRef.set(b);174b.vargFromLong(CLinker.C_LONG_LONG, 42L);175}, ResourceScope.newImplicitScope());176vaListBuilder = builderRef.get();177}178}179180static final Map<Class<?>, Object[]> REPLACEMENT_VALUES = new HashMap<>();181182@SafeVarargs183static <Z> void addReplacements(Class<Z> carrier, Z... value) {184REPLACEMENT_VALUES.put(carrier, value);185}186187static {188addReplacements(Collection.class, null, Stream.of(new Object[] { null }).collect(Collectors.toList()));189addReplacements(List.class, null, Stream.of(new Object[] { null }).collect(Collectors.toList()));190addReplacements(Set.class, null, Stream.of(new Object[] { null }).collect(Collectors.toSet()));191}192193@Test(dataProvider = "cases")194public void testNulls(String testName, @NoInjection Method meth, Object receiver, Object[] args) {195try {196meth.invoke(receiver, args);197fail("Method invocation completed normally");198} catch (InvocationTargetException ex) {199Class<?> cause = ex.getCause().getClass();200assertEquals(cause, NullPointerException.class, "got " + cause.getName() + " - expected NullPointerException");201} catch (Throwable ex) {202fail("Unexpected exception: " + ex);203}204}205206@DataProvider(name = "cases")207static Iterator<Object[]> cases() {208List<Object[]> cases = new ArrayList<>();209for (Class<?> clazz : CLASSES) {210for (Method m : clazz.getMethods()) {211if (OBJECT_METHODS.contains(m.getName())) continue;212boolean isStatic = (m.getModifiers() & Modifier.STATIC) != 0;213List<Integer> refIndices = new ArrayList<>();214for (int i = 0; i < m.getParameterCount(); i++) {215Class<?> param = m.getParameterTypes()[i];216if (!param.isPrimitive()) {217refIndices.add(i);218}219}220for (int i : refIndices) {221Object[] replacements = replacements(m.getParameterTypes()[i]);222for (int r = 0 ; r < replacements.length ; r++) {223String testName = clazz.getName() + "/" + shortSig(m) + "/" + i + "/" + r;224if (EXCLUDE_LIST.contains(testName)) continue;225Object[] args = new Object[m.getParameterCount()];226for (int j = 0; j < args.length; j++) {227args[j] = defaultValue(m.getParameterTypes()[j]);228}229args[i] = replacements[r];230Object receiver = isStatic ? null : defaultValue(clazz);231cases.add(new Object[]{testName, m, receiver, args});232}233}234}235}236return cases.iterator();237};238239static String shortSig(Method m) {240StringJoiner sj = new StringJoiner(",", m.getName() + "(", ")");241for (Class<?> parameterType : m.getParameterTypes()) {242sj.add(parameterType.getTypeName());243}244return sj.toString();245}246247static Object defaultValue(Class<?> carrier) {248if (carrier.isArray()) {249return Array.newInstance(carrier.componentType(), 0);250}251Object value = DEFAULT_VALUES.get(carrier);252if (value == null) {253throw new UnsupportedOperationException(carrier.getName());254}255return value;256}257258static Object[] replacements(Class<?> carrier) {259if (carrier.isArray() && !carrier.getComponentType().isPrimitive()) {260Object arr = Array.newInstance(carrier.componentType(), 1);261Array.set(arr, 0, null);262return new Object[] { null, arr };263}264return REPLACEMENT_VALUES.getOrDefault(carrier, new Object[] { null });265}266}267268269