1 /*
  2  * Copyright (c) 2022, 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;
 26 
 27 import java.lang.foreign.FunctionDescriptor;
 28 import java.lang.foreign.Linker;
 29 import java.util.HashMap;
 30 import java.util.Map;
 31 import java.util.Objects;
 32 import java.util.Set;
 33 import java.util.function.BiConsumer;
 34 import java.util.stream.Stream;
 35 
 36 public class LinkerOptions {
 37 
 38     private static final LinkerOptions EMPTY = new LinkerOptions(Map.of());
 39     private final Map<Class<?>, LinkerOptionImpl> optionsMap;
 40 
 41     private LinkerOptions(Map<Class<?>, LinkerOptionImpl> optionsMap) {
 42         this.optionsMap = optionsMap;
 43     }
 44 
 45     public static LinkerOptions forDowncall(FunctionDescriptor desc, Linker.Option... options) {
 46         return forShared(LinkerOptionImpl::validateForDowncall, desc, options);
 47     }
 48 
 49     public static LinkerOptions forUpcall(FunctionDescriptor desc, Linker.Option[] options) {
 50         return forShared(LinkerOptionImpl::validateForUpcall, desc, options);
 51     }
 52 
 53     private static LinkerOptions forShared(BiConsumer<LinkerOptionImpl, FunctionDescriptor> validator,
 54                                            FunctionDescriptor desc, Linker.Option... options) {
 55        Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
 56 
 57         for (Linker.Option option : options) {
 58             if (optionMap.containsKey(option.getClass())) {
 59                 throw new IllegalArgumentException("Duplicate option: " + option);
 60             }
 61             LinkerOptionImpl opImpl = (LinkerOptionImpl) option;
 62             validator.accept(opImpl, desc);
 63             optionMap.put(option.getClass(), opImpl);
 64         }
 65 
 66         return new LinkerOptions(optionMap);
 67     }
 68 
 69     public static LinkerOptions empty() {
 70         return EMPTY;
 71     }
 72 
 73     private <T extends Linker.Option> T getOption(Class<T> type) {
 74         return type.cast(optionsMap.get(type));
 75     }
 76 
 77     public boolean isVarargsIndex(int argIndex) {
 78         FirstVariadicArg fva = getOption(FirstVariadicArg.class);
 79         return fva != null && argIndex >= fva.index();
 80     }
 81 
 82     public boolean hasCapturedCallState() {
 83         return getOption(CaptureCallState.class) != null;
 84     }
 85 
 86     public Stream<CapturableState> capturedCallState() {
 87         CaptureCallState stl = getOption(CaptureCallState.class);
 88         return stl == null ? Stream.empty() : stl.saved().stream();
 89     }
 90 
 91     public boolean isVariadicFunction() {
 92         FirstVariadicArg fva = getOption(FirstVariadicArg.class);
 93         return fva != null;
 94     }
 95 
 96     public int firstVariadicArgIndex() {
 97         return getOption(FirstVariadicArg.class).index();
 98     }
 99 
100     public boolean isTrivial() {
101         IsTrivial it = getOption(IsTrivial.class);
102         return it != null;
103     }
104 
105     @Override
106     public boolean equals(Object o) {
107         if (this == o) return true;
108         return o instanceof LinkerOptions that
109                 && Objects.equals(optionsMap, that.optionsMap);
110     }
111 
112     @Override
113     public int hashCode() {
114         return Objects.hash(optionsMap);
115     }
116 
117     public sealed interface LinkerOptionImpl extends Linker.Option
118             permits CaptureCallState, FirstVariadicArg, IsTrivial {
119         default void validateForDowncall(FunctionDescriptor descriptor) {
120             throw new IllegalArgumentException("Not supported for downcall: " + this);
121         }
122 
123         default void validateForUpcall(FunctionDescriptor descriptor) {
124             throw new IllegalArgumentException("Not supported for upcall: " + this);
125         }
126     }
127 
128     public record FirstVariadicArg(int index) implements LinkerOptionImpl {
129         @Override
130         public void validateForDowncall(FunctionDescriptor descriptor) {
131             if (index < 0 || index > descriptor.argumentLayouts().size()) {
132                 throw new IllegalArgumentException("Index '" + index + "' not in bounds for descriptor: " + descriptor);
133             }
134         }
135     }
136 
137     public record CaptureCallState(Set<CapturableState> saved) implements LinkerOptionImpl {
138         @Override
139         public void validateForDowncall(FunctionDescriptor descriptor) {
140             // done during construction
141         }
142     }
143 
144     public record IsTrivial() implements LinkerOptionImpl {
145         public static IsTrivial INSTANCE = new IsTrivial();
146 
147         @Override
148         public void validateForDowncall(FunctionDescriptor descriptor) {
149             // always allowed
150         }
151     }
152 }