1 /* 2 * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.internal.foreign.abi.fallback; 26 27 import jdk.internal.foreign.Utils; 28 29 import java.lang.foreign.Arena; 30 import java.lang.foreign.GroupLayout; 31 import java.lang.foreign.MemoryLayout; 32 import java.lang.foreign.MemorySegment; 33 import java.lang.foreign.PaddingLayout; 34 import java.lang.foreign.SequenceLayout; 35 import java.lang.foreign.StructLayout; 36 import java.lang.foreign.UnionLayout; 37 import java.lang.foreign.ValueLayout; 38 import java.lang.invoke.VarHandle; 39 import java.util.Collections; 40 import java.util.Comparator; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Objects; 44 import java.util.function.Predicate; 45 46 import static java.lang.foreign.ValueLayout.ADDRESS; 47 import static java.lang.foreign.ValueLayout.JAVA_INT; 48 import static java.lang.foreign.ValueLayout.JAVA_LONG; 49 import static java.lang.foreign.ValueLayout.JAVA_SHORT; 50 51 /** 52 * typedef struct _ffi_type 53 * { 54 * size_t size; 55 * unsigned short alignment; 56 * unsigned short type; 57 * struct _ffi_type **elements; 58 * } ffi_type; 59 */ 60 class FFIType { 61 private static final ValueLayout SIZE_T = switch ((int) ADDRESS.byteSize()) { 62 case 8 -> JAVA_LONG; 63 case 4 -> JAVA_INT; 64 default -> throw new IllegalStateException("Address size not supported: " + ADDRESS.byteSize()); 65 }; 66 private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT; 67 private static final StructLayout LAYOUT = Utils.computePaddedStructLayout( 68 SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements")); 69 70 private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type")); 71 private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements")); 72 private static final VarHandle VH_SIZE_T_ARRAY = SIZE_T.arrayElementVarHandle(); 73 74 private static MemorySegment make(List<MemoryLayout> elements, FFIABI abi, Arena scope) { 75 MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize()); 76 int i = 0; 77 for (; i < elements.size(); i++) { 78 MemoryLayout elementLayout = elements.get(i); 79 MemorySegment elementType = toFFIType(elementLayout, abi, scope); 80 elementsSeg.setAtIndex(ADDRESS, i, elementType); 81 } 82 // elements array is null-terminated 83 elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL); 84 85 MemorySegment ffiType = scope.allocate(LAYOUT); 86 VH_TYPE.set(ffiType, LibFallback.structTag()); 87 VH_ELEMENTS.set(ffiType, elementsSeg); 88 89 return ffiType; 90 } 91 92 private static final Map<Class<?>, MemorySegment> CARRIER_TO_TYPE = Map.of( 93 boolean.class, LibFallback.uint8Type(), 94 byte.class, LibFallback.sint8Type(), 95 short.class, LibFallback.sint16Type(), 96 char.class, LibFallback.uint16Type(), 97 int.class, LibFallback.sint32Type(), 98 long.class, LibFallback.sint64Type(), 99 float.class, LibFallback.floatType(), 100 double.class, LibFallback.doubleType(), 101 MemorySegment.class, LibFallback.pointerType() 102 ); 103 104 static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, Arena scope) { 105 if (layout instanceof GroupLayout grpl) { 106 if (grpl instanceof StructLayout strl) { 107 // libffi doesn't want our padding 108 List<MemoryLayout> filteredLayouts = strl.memberLayouts().stream() 109 .filter(Predicate.not(PaddingLayout.class::isInstance)) 110 .toList(); 111 MemorySegment structType = make(filteredLayouts, abi, scope); 112 verifyStructType(strl, filteredLayouts, structType, abi); 113 return structType; 114 } 115 assert grpl instanceof UnionLayout; 116 // JDK-8301800 117 throw new IllegalArgumentException("Fallback linker does not support by-value unions: " + grpl); 118 } else if (layout instanceof SequenceLayout sl) { 119 List<MemoryLayout> elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout()); 120 return make(elements, abi, scope); 121 } 122 return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier())); 123 } 124 125 // verify layout against what libffi sets 126 private static void verifyStructType(StructLayout structLayout, List<MemoryLayout> filteredLayouts, MemorySegment structType, 127 FFIABI abi) { 128 try (Arena verifyArena = Arena.ofConfined()) { 129 MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size()); 130 LibFallback.getStructOffsets(structType, offsetsOut, abi); 131 long expectedOffset = 0; 132 int offsetIdx = 0; 133 for (MemoryLayout element : structLayout.memberLayouts()) { 134 if (!(element instanceof PaddingLayout)) { 135 long ffiOffset = (long) VH_SIZE_T_ARRAY.get(offsetsOut, offsetIdx++); 136 if (ffiOffset != expectedOffset) { 137 throw new IllegalArgumentException("Invalid group layout." + 138 " Offset of '" + element.name().orElse("<unnamed>") 139 + "': " + expectedOffset + " != " + ffiOffset); 140 } 141 } 142 expectedOffset += element.byteSize(); 143 } 144 } 145 } 146 }