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.List; 41 import java.util.Map; 42 import java.util.Objects; 43 import java.util.function.Predicate; 44 45 import static java.lang.foreign.ValueLayout.ADDRESS; 46 import static java.lang.foreign.ValueLayout.JAVA_BYTE; 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 static final ValueLayout SIZE_T = layoutFor((int)ADDRESS.byteSize()); 62 private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT; 63 private static final StructLayout LAYOUT = Utils.computePaddedStructLayout( 64 SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements")); 65 66 private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type")); 67 private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements")); 68 private static final VarHandle VH_SIZE_T = SIZE_T.varHandle(); 69 70 private static MemorySegment make(List<MemoryLayout> elements, FFIABI abi, Arena scope) { 71 MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize()); 72 int i = 0; 73 for (; i < elements.size(); i++) { 74 MemoryLayout elementLayout = elements.get(i); 75 MemorySegment elementType = toFFIType(elementLayout, abi, scope); 76 elementsSeg.setAtIndex(ADDRESS, i, elementType); 77 } 78 // elements array is null-terminated 79 elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL); 80 81 MemorySegment ffiType = scope.allocate(LAYOUT); 82 VH_TYPE.set(ffiType, 0L, LibFallback.structTag()); 83 VH_ELEMENTS.set(ffiType, 0L, elementsSeg); 84 85 return ffiType; 86 } 87 88 private static final Map<Class<?>, MemorySegment> CARRIER_TO_TYPE = Map.of( 89 boolean.class, LibFallback.uint8Type(), 90 byte.class, LibFallback.sint8Type(), 91 short.class, LibFallback.sint16Type(), 92 char.class, LibFallback.uint16Type(), 93 int.class, LibFallback.sint32Type(), 94 long.class, LibFallback.sint64Type(), 95 float.class, LibFallback.floatType(), 96 double.class, LibFallback.doubleType(), 97 MemorySegment.class, LibFallback.pointerType() 98 ); 99 100 static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, Arena scope) { 101 if (layout instanceof GroupLayout grpl) { 102 if (grpl instanceof StructLayout strl) { 103 // libffi doesn't want our padding 104 List<MemoryLayout> filteredLayouts = strl.memberLayouts().stream() 105 .filter(Predicate.not(PaddingLayout.class::isInstance)) 106 .toList(); 107 MemorySegment structType = make(filteredLayouts, abi, scope); 108 verifyStructType(strl, filteredLayouts, structType, abi); 109 return structType; 110 } 111 assert grpl instanceof UnionLayout; 112 // JDK-8301800 113 throw new IllegalArgumentException("Fallback linker does not support by-value unions: " + grpl); 114 } else if (layout instanceof SequenceLayout sl) { 115 List<MemoryLayout> elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout()); 116 return make(elements, abi, scope); 117 } 118 return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier())); 119 } 120 121 // verify layout against what libffi sets 122 private static void verifyStructType(StructLayout structLayout, List<MemoryLayout> filteredLayouts, MemorySegment structType, 123 FFIABI abi) { 124 try (Arena verifyArena = Arena.ofConfined()) { 125 MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size()); 126 LibFallback.getStructOffsets(structType, offsetsOut, abi); 127 long expectedOffset = 0; 128 int offsetIdx = 0; 129 for (MemoryLayout element : structLayout.memberLayouts()) { 130 if (!(element instanceof PaddingLayout)) { 131 long ffiOffset = sizeTAtIndex(offsetsOut, offsetIdx++); 132 if (ffiOffset != expectedOffset) { 133 throw new IllegalArgumentException("Invalid group layout." + 134 " Offset of '" + element.name().orElse("<unnamed>") 135 + "': " + expectedOffset + " != " + ffiOffset); 136 } 137 } 138 expectedOffset += element.byteSize(); 139 } 140 } 141 } 142 143 static ValueLayout layoutFor(int byteSize) { 144 return switch (byteSize) { 145 case 1 -> JAVA_BYTE; 146 case 2 -> JAVA_SHORT; 147 case 4 -> JAVA_INT; 148 case 8 -> JAVA_LONG; 149 default -> throw new IllegalStateException("Unsupported size: " + byteSize); 150 }; 151 } 152 153 private static long sizeTAtIndex(MemorySegment segment, int index) { 154 long offset = SIZE_T.scale(0, index); 155 if (VH_SIZE_T.varType() == long.class) { 156 return (long) VH_SIZE_T.get(segment, offset); 157 } else { 158 return (int) VH_SIZE_T.get(segment, offset); // 'erase' to long 159 } 160 } 161 }