Path: blob/master/test/jdk/java/foreign/CallGeneratorHelper.java
66643 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*22*/2324import jdk.incubator.foreign.GroupLayout;25import jdk.incubator.foreign.MemoryAddress;26import jdk.incubator.foreign.MemoryLayout;27import jdk.incubator.foreign.MemorySegment;28import jdk.incubator.foreign.ResourceScope;29import jdk.incubator.foreign.SegmentAllocator;30import jdk.incubator.foreign.ValueLayout;3132import java.lang.invoke.VarHandle;33import java.util.ArrayList;34import java.util.List;35import java.util.Stack;36import java.util.function.Consumer;37import java.util.stream.Collectors;38import java.util.stream.IntStream;3940import org.testng.annotations.*;4142import static jdk.incubator.foreign.CLinker.*;43import static org.testng.Assert.*;4445public class CallGeneratorHelper extends NativeTestHelper {4647static SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());4849static final int SAMPLE_FACTOR = Integer.parseInt((String)System.getProperties().getOrDefault("generator.sample.factor", "-1"));5051static final int MAX_FIELDS = 3;52static final int MAX_PARAMS = 3;53static final int CHUNK_SIZE = 600;5455public static void assertStructEquals(MemorySegment actual, MemorySegment expected, MemoryLayout layout) {56assertEquals(actual.byteSize(), expected.byteSize());57GroupLayout g = (GroupLayout) layout;58for (MemoryLayout field : g.memberLayouts()) {59if (field instanceof ValueLayout) {60VarHandle vh = g.varHandle(vhCarrier(field), MemoryLayout.PathElement.groupElement(field.name().orElseThrow()));61assertEquals(vh.get(actual), vh.get(expected));62}63}64}6566private static Class<?> vhCarrier(MemoryLayout layout) {67if (layout instanceof ValueLayout) {68if (isIntegral(layout)) {69if (layout.bitSize() == 64) {70return long.class;71}72return int.class;73} else if (layout.bitSize() == 32) {74return float.class;75}76return double.class;77} else {78throw new IllegalStateException("Unexpected layout: " + layout);79}80}8182enum Ret {83VOID,84NON_VOID85}8687enum StructFieldType {88INT("int", C_INT),89FLOAT("float", C_FLOAT),90DOUBLE("double", C_DOUBLE),91POINTER("void*", C_POINTER);9293final String typeStr;94final MemoryLayout layout;9596StructFieldType(String typeStr, MemoryLayout layout) {97this.typeStr = typeStr;98this.layout = layout;99}100101MemoryLayout layout() {102return layout;103}104105@SuppressWarnings("unchecked")106static List<List<StructFieldType>>[] perms = new List[10];107108static List<List<StructFieldType>> perms(int i) {109if (perms[i] == null) {110perms[i] = generateTest(i, values());111}112return perms[i];113}114}115116enum ParamType {117INT("int", C_INT),118FLOAT("float", C_FLOAT),119DOUBLE("double", C_DOUBLE),120POINTER("void*", C_POINTER),121STRUCT("struct S", null);122123private final String typeStr;124private final MemoryLayout layout;125126ParamType(String typeStr, MemoryLayout layout) {127this.typeStr = typeStr;128this.layout = layout;129}130131String type(List<StructFieldType> fields) {132return this == STRUCT ?133typeStr + "_" + sigCode(fields) :134typeStr;135}136137MemoryLayout layout(List<StructFieldType> fields) {138if (this == STRUCT) {139long offset = 0L;140List<MemoryLayout> layouts = new ArrayList<>();141for (StructFieldType field : fields) {142MemoryLayout l = field.layout();143long padding = offset % l.bitSize();144if (padding != 0) {145layouts.add(MemoryLayout.paddingLayout(padding));146offset += padding;147}148layouts.add(l.withName("field" + offset));149offset += l.bitSize();150}151return MemoryLayout.structLayout(layouts.toArray(new MemoryLayout[0]));152} else {153return layout;154}155}156157@SuppressWarnings("unchecked")158static List<List<ParamType>>[] perms = new List[10];159160static List<List<ParamType>> perms(int i) {161if (perms[i] == null) {162perms[i] = generateTest(i, values());163}164return perms[i];165}166}167168static <Z> List<List<Z>> generateTest(int i, Z[] elems) {169List<List<Z>> res = new ArrayList<>();170generateTest(i, new Stack<>(), elems, res);171return res;172}173174static <Z> void generateTest(int i, Stack<Z> combo, Z[] elems, List<List<Z>> results) {175if (i == 0) {176results.add(new ArrayList<>(combo));177} else {178for (Z z : elems) {179combo.push(z);180generateTest(i - 1, combo, elems, results);181combo.pop();182}183}184}185186@DataProvider(name = "functions")187public static Object[][] functions() {188int functions = 0;189List<Object[]> downcalls = new ArrayList<>();190for (Ret r : Ret.values()) {191for (int i = 0; i <= MAX_PARAMS; i++) {192if (r != Ret.VOID && i == 0) continue;193for (List<ParamType> ptypes : ParamType.perms(i)) {194String retCode = r == Ret.VOID ? "V" : ptypes.get(0).name().charAt(0) + "";195String sigCode = sigCode(ptypes);196if (ptypes.contains(ParamType.STRUCT)) {197for (int j = 1; j <= MAX_FIELDS; j++) {198for (List<StructFieldType> fields : StructFieldType.perms(j)) {199String structCode = sigCode(fields);200int count = functions;201int fCode = functions++ / CHUNK_SIZE;202String fName = String.format("f%d_%s_%s_%s", fCode, retCode, sigCode, structCode);203if (SAMPLE_FACTOR == -1 || (count % SAMPLE_FACTOR) == 0) {204downcalls.add(new Object[]{count, fName, r, ptypes, fields});205}206}207}208} else {209String structCode = sigCode(List.<StructFieldType>of());210int count = functions;211int fCode = functions++ / CHUNK_SIZE;212String fName = String.format("f%d_%s_%s_%s", fCode, retCode, sigCode, structCode);213if (SAMPLE_FACTOR == -1 || (count % SAMPLE_FACTOR) == 0) {214downcalls.add(new Object[]{count, fName, r, ptypes, List.of()});215}216}217}218}219}220return downcalls.toArray(new Object[0][]);221}222223static <Z extends Enum<Z>> String sigCode(List<Z> elems) {224return elems.stream().map(p -> p.name().charAt(0) + "").collect(Collectors.joining());225}226227static void generateStructDecl(List<StructFieldType> fields) {228String structCode = sigCode(fields);229List<String> fieldDecls = new ArrayList<>();230for (int i = 0 ; i < fields.size() ; i++) {231fieldDecls.add(String.format("%s p%d;", fields.get(i).typeStr, i));232}233String res = String.format("struct S_%s { %s };", structCode,234fieldDecls.stream().collect(Collectors.joining(" ")));235System.out.println(res);236}237238/* this can be used to generate the test header/implementation */239public static void main(String[] args) {240boolean header = args.length > 0 && args[0].equals("header");241boolean upcall = args.length > 1 && args[1].equals("upcall");242if (upcall) {243generateUpcalls(header);244} else {245generateDowncalls(header);246}247}248249static void generateDowncalls(boolean header) {250if (header) {251System.out.println(252"#ifdef _WIN64\n" +253"#define EXPORT __declspec(dllexport)\n" +254"#else\n" +255"#define EXPORT\n" +256"#endif\n"257);258259for (int j = 1; j <= MAX_FIELDS; j++) {260for (List<StructFieldType> fields : StructFieldType.perms(j)) {261generateStructDecl(fields);262}263}264} else {265System.out.println(266"#include \"libh\"\n" +267"#ifdef __clang__\n" +268"#pragma clang optimize off\n" +269"#elif defined __GNUC__\n" +270"#pragma GCC optimize (\"O0\")\n" +271"#elif defined _MSC_BUILD\n" +272"#pragma optimize( \"\", off )\n" +273"#endif\n"274);275}276277for (Object[] downcall : functions()) {278String fName = (String)downcall[0];279Ret r = (Ret)downcall[1];280@SuppressWarnings("unchecked")281List<ParamType> ptypes = (List<ParamType>)downcall[2];282@SuppressWarnings("unchecked")283List<StructFieldType> fields = (List<StructFieldType>)downcall[3];284generateDowncallFunction(fName, r, ptypes, fields, header);285}286}287288static void generateDowncallFunction(String fName, Ret ret, List<ParamType> params, List<StructFieldType> fields, boolean declOnly) {289String retType = ret == Ret.VOID ? "void" : params.get(0).type(fields);290List<String> paramDecls = new ArrayList<>();291for (int i = 0 ; i < params.size() ; i++) {292paramDecls.add(String.format("%s p%d", params.get(i).type(fields), i));293}294String sig = paramDecls.isEmpty() ?295"void" :296paramDecls.stream().collect(Collectors.joining(", "));297String body = ret == Ret.VOID ? "{ }" : "{ return p0; }";298String res = String.format("EXPORT %s f%s(%s) %s", retType, fName,299sig, declOnly ? ";" : body);300System.out.println(res);301}302303static void generateUpcalls(boolean header) {304if (header) {305System.out.println(306"#ifdef _WIN64\n" +307"#define EXPORT __declspec(dllexport)\n" +308"#else\n" +309"#define EXPORT\n" +310"#endif\n"311);312313for (int j = 1; j <= MAX_FIELDS; j++) {314for (List<StructFieldType> fields : StructFieldType.perms(j)) {315generateStructDecl(fields);316}317}318} else {319System.out.println(320"#include \"libh\"\n" +321"#ifdef __clang__\n" +322"#pragma clang optimize off\n" +323"#elif defined __GNUC__\n" +324"#pragma GCC optimize (\"O0\")\n" +325"#elif defined _MSC_BUILD\n" +326"#pragma optimize( \"\", off )\n" +327"#endif\n"328);329}330331for (Object[] downcall : functions()) {332String fName = (String)downcall[0];333Ret r = (Ret)downcall[1];334@SuppressWarnings("unchecked")335List<ParamType> ptypes = (List<ParamType>)downcall[2];336@SuppressWarnings("unchecked")337List<StructFieldType> fields = (List<StructFieldType>)downcall[3];338generateUpcallFunction(fName, r, ptypes, fields, header);339}340}341342static void generateUpcallFunction(String fName, Ret ret, List<ParamType> params, List<StructFieldType> fields, boolean declOnly) {343String retType = ret == Ret.VOID ? "void" : params.get(0).type(fields);344List<String> paramDecls = new ArrayList<>();345for (int i = 0 ; i < params.size() ; i++) {346paramDecls.add(String.format("%s p%d", params.get(i).type(fields), i));347}348String paramNames = IntStream.range(0, params.size())349.mapToObj(i -> "p" + i)350.collect(Collectors.joining(","));351String sig = paramDecls.isEmpty() ?352"" :353paramDecls.stream().collect(Collectors.joining(", ")) + ", ";354String body = String.format(ret == Ret.VOID ? "{ cb(%s); }" : "{ return cb(%s); }", paramNames);355List<String> paramTypes = params.stream().map(p -> p.type(fields)).collect(Collectors.toList());356String cbSig = paramTypes.isEmpty() ?357"void" :358paramTypes.stream().collect(Collectors.joining(", "));359String cbParam = String.format("%s (*cb)(%s)",360retType, cbSig);361362String res = String.format("EXPORT %s %s(%s %s) %s", retType, fName,363sig, cbParam, declOnly ? ";" : body);364System.out.println(res);365}366367//helper methods368369@SuppressWarnings("unchecked")370static Object makeArg(MemoryLayout layout, List<Consumer<Object>> checks, boolean check) throws ReflectiveOperationException {371if (layout instanceof GroupLayout) {372MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());373initStruct(segment, (GroupLayout)layout, checks, check);374return segment;375} else if (isPointer(layout)) {376MemorySegment segment = MemorySegment.allocateNative(1, ResourceScope.newImplicitScope());377if (check) {378checks.add(o -> {379try {380assertEquals(o, segment.address());381} catch (Throwable ex) {382throw new IllegalStateException(ex);383}384});385}386return segment.address();387} else if (layout instanceof ValueLayout) {388if (isIntegral(layout)) {389if (check) {390checks.add(o -> assertEquals(o, 42));391}392return 42;393} else if (layout.bitSize() == 32) {394if (check) {395checks.add(o -> assertEquals(o, 12f));396}397return 12f;398} else {399if (check) {400checks.add(o -> assertEquals(o, 24d));401}402return 24d;403}404} else {405throw new IllegalStateException("Unexpected layout: " + layout);406}407}408409static void initStruct(MemorySegment str, GroupLayout g, List<Consumer<Object>> checks, boolean check) throws ReflectiveOperationException {410for (MemoryLayout l : g.memberLayouts()) {411if (l.isPadding()) continue;412VarHandle accessor = g.varHandle(structFieldCarrier(l), MemoryLayout.PathElement.groupElement(l.name().get()));413List<Consumer<Object>> fieldsCheck = new ArrayList<>();414Object value = makeArg(l, fieldsCheck, check);415if (isPointer(l)) {416value = ((MemoryAddress)value).toRawLongValue();417}418//set value419accessor.set(str, value);420//add check421if (check) {422assertTrue(fieldsCheck.size() == 1);423checks.add(o -> {424MemorySegment actual = (MemorySegment)o;425try {426if (isPointer(l)) {427fieldsCheck.get(0).accept(MemoryAddress.ofLong((long)accessor.get(actual)));428} else {429fieldsCheck.get(0).accept(accessor.get(actual));430}431} catch (Throwable ex) {432throw new IllegalStateException(ex);433}434});435}436}437}438439static Class<?> structFieldCarrier(MemoryLayout layout) {440if (isPointer(layout)) {441return long.class;442} else if (layout instanceof ValueLayout) {443if (isIntegral(layout)) {444return int.class;445} else if (layout.bitSize() == 32) {446return float.class;447} else {448return double.class;449}450} else {451throw new IllegalStateException("Unexpected layout: " + layout);452}453}454455static Class<?> paramCarrier(MemoryLayout layout) {456if (layout instanceof GroupLayout) {457return MemorySegment.class;458} if (isPointer(layout)) {459return MemoryAddress.class;460} else if (layout instanceof ValueLayout) {461if (isIntegral(layout)) {462return int.class;463} else if (layout.bitSize() == 32) {464return float.class;465} else {466return double.class;467}468} else {469throw new IllegalStateException("Unexpected layout: " + layout);470}471}472}473474475