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 }