1 /*
  2  *  Copyright (c) 2020, 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.incubator.foreign;
 26 
 27 import java.lang.constant.Constable;
 28 import java.lang.constant.ConstantDesc;
 29 import java.lang.constant.ConstantDescs;
 30 import java.lang.constant.DynamicConstantDesc;
 31 import java.util.ArrayList;
 32 import java.util.Arrays;
 33 import java.util.List;
 34 import java.util.Objects;
 35 import java.util.Optional;
 36 import java.util.stream.Collectors;
 37 import java.util.stream.Stream;
 38 
 39 /**
 40  * A function descriptor is made up of zero or more argument layouts and zero or one return layout. A function descriptor
 41  * is used to model the signature of foreign functions.
 42  *
 43  * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
 44  * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
 45  */
 46 public sealed class FunctionDescriptor implements Constable permits FunctionDescriptor.VariadicFunction {
 47 
 48     private final MemoryLayout resLayout;
 49     private final MemoryLayout[] argLayouts;
 50 
 51     private FunctionDescriptor(MemoryLayout resLayout, MemoryLayout... argLayouts) {
 52         this.resLayout = resLayout;
 53         this.argLayouts = argLayouts;
 54     }
 55 
 56     /**
 57      * Returns the return layout associated with this function.
 58      * @return the return layout.
 59      */
 60     public Optional<MemoryLayout> returnLayout() {
 61         return Optional.ofNullable(resLayout);
 62     }
 63 
 64     /**
 65      * Returns the argument layouts associated with this function.
 66      * @return the argument layouts.
 67      */
 68     public List<MemoryLayout> argumentLayouts() {
 69         return Arrays.asList(argLayouts);
 70     }
 71 
 72     /**
 73      * Create a function descriptor with given return and argument layouts.
 74      * @param resLayout the return layout.
 75      * @param argLayouts the argument layouts.
 76      * @return the new function descriptor.
 77      */
 78     public static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) {
 79         Objects.requireNonNull(resLayout);
 80         Objects.requireNonNull(argLayouts);
 81         Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
 82         return new FunctionDescriptor(resLayout, argLayouts);
 83     }
 84 
 85     /**
 86      * Create a function descriptor with given argument layouts and no return layout.
 87      * @param argLayouts the argument layouts.
 88      * @return the new function descriptor.
 89      */
 90     public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) {
 91         Objects.requireNonNull(argLayouts);
 92         Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
 93         return new FunctionDescriptor(null, argLayouts);
 94     }
 95 
 96     /**
 97      * Obtain a specialized variadic function descriptor, by appending given variadic layouts to this
 98      * function descriptor argument layouts. The resulting function descriptor can report the position
 99      * of the {@linkplain #firstVariadicArgumentIndex() first variadic argument}, and cannot be altered
100      * in any way: for instance, calling {@link #withReturnLayout(MemoryLayout)} on the resulting descriptor
101      * will throw an {@link UnsupportedOperationException}.
102      * @param variadicLayouts the variadic argument layouts to be appended to this descriptor argument layouts.
103      * @return a new variadic function descriptor, or this descriptor if {@code variadicLayouts.length == 0}.
104      */
105     public FunctionDescriptor asVariadic(MemoryLayout... variadicLayouts) {
106         Objects.requireNonNull(variadicLayouts);
107         Arrays.stream(variadicLayouts).forEach(Objects::requireNonNull);
108         return variadicLayouts.length == 0 ? this : new VariadicFunction(this, variadicLayouts);
109     }
110 
111     /**
112      * The index of the first variadic argument layout (where defined).
113      * @return The index of the first variadic argument layout, or {@code -1} if this is not a
114      * {@linkplain #asVariadic(MemoryLayout...) variadic} layout.
115      */
116     public int firstVariadicArgumentIndex() {
117         return -1;
118     }
119 
120     /**
121      * Create a new function descriptor with the given argument layouts appended to the argument layout array
122      * of this function descriptor.
123      * @param addedLayouts the argument layouts to append.
124      * @return the new function descriptor.
125      */
126     public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) {
127         Objects.requireNonNull(addedLayouts);
128         Arrays.stream(addedLayouts).forEach(Objects::requireNonNull);
129         MemoryLayout[] newLayouts = Arrays.copyOf(argLayouts, argLayouts.length + addedLayouts.length);
130         System.arraycopy(addedLayouts, 0, newLayouts, argLayouts.length, addedLayouts.length);
131         return new FunctionDescriptor(resLayout, newLayouts);
132     }
133 
134     /**
135      * Create a new function descriptor with the given memory layout as the new return layout.
136      * @param newReturn the new return layout.
137      * @return the new function descriptor.
138      */
139     public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) {
140         Objects.requireNonNull(newReturn);
141         return new FunctionDescriptor(newReturn, argLayouts);
142     }
143 
144     /**
145      * Create a new function descriptor with the return layout dropped.
146      * @return the new function descriptor.
147      */
148     public FunctionDescriptor withVoidReturnLayout() {
149         return new FunctionDescriptor(null, argLayouts);
150     }
151 
152     /**
153      * Returns a string representation of this function descriptor.
154      * @return a string representation of this function descriptor.
155      */
156     @Override
157     public String toString() {
158         return String.format("(%s)%s",
159                 Stream.of(argLayouts)
160                         .map(Object::toString)
161                         .collect(Collectors.joining()),
162                 returnLayout().map(Object::toString).orElse("v"));
163     }
164 
165     /**
166      * Compares the specified object with this function descriptor for equality. Returns {@code true} if and only if the specified
167      * object is also a function descriptor, and all the following conditions are met:
168      * <ul>
169      *     <li>the two function descriptors have equals return layouts (see {@link MemoryLayout#equals(Object)}), or both have no return layout</li>
170      *     <li>the two function descriptors have argument layouts that are pair-wise equal (see {@link MemoryLayout#equals(Object)})
171      * </ul>
172      *
173      * @param other the object to be compared for equality with this function descriptor.
174      * @return {@code true} if the specified object is equal to this function descriptor.
175      */
176     @Override
177     public boolean equals(Object other) {
178         if (this == other) {
179             return true;
180         }
181         if (!(other instanceof FunctionDescriptor f)) {
182             return false;
183         }
184         return Objects.equals(resLayout, f.resLayout) && Arrays.equals(argLayouts, f.argLayouts);
185     }
186 
187     /**
188      * Returns the hash code value for this function descriptor.
189      * @return the hash code value for this function descriptor.
190      */
191     @Override
192     public int hashCode() {
193         int hashCode = Arrays.hashCode(argLayouts);
194         return resLayout == null ? hashCode : resLayout.hashCode() ^ hashCode;
195     }
196 
197     @Override
198     public Optional<DynamicConstantDesc<FunctionDescriptor>> describeConstable() {
199         List<ConstantDesc> constants = new ArrayList<>();
200         constants.add(resLayout == null ? AbstractLayout.MH_VOID_FUNCTION : AbstractLayout.MH_FUNCTION);
201         if (resLayout != null) {
202             constants.add(resLayout.describeConstable().get());
203         }
204         for (MemoryLayout argLayout : argLayouts) {
205             constants.add(argLayout.describeConstable().get());
206         }
207         return Optional.of(DynamicConstantDesc.ofNamed(
208                     ConstantDescs.BSM_INVOKE, "function", AbstractLayout.CD_FUNCTION_DESC, constants.toArray(new ConstantDesc[0])));
209     }
210 
211     static final class VariadicFunction extends FunctionDescriptor {
212 
213         private final int firstVariadicIndex;
214 
215         public VariadicFunction(FunctionDescriptor descriptor, MemoryLayout... argLayouts) {
216             super(descriptor.returnLayout().orElse(null),
217                     Stream.concat(descriptor.argumentLayouts().stream(), Stream.of(argLayouts)).toArray(MemoryLayout[]::new));
218             this.firstVariadicIndex = descriptor.argumentLayouts().size();
219         }
220 
221         public boolean isVariadicIndex(int pos) {
222             return pos >= firstVariadicIndex;
223         }
224 
225         public int firstVariadicArgumentIndex() {
226             return firstVariadicIndex;
227         }
228 
229         @Override
230         public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) {
231             throw new UnsupportedOperationException();
232         }
233 
234         @Override
235         public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) {
236             throw new UnsupportedOperationException();
237         }
238 
239         @Override
240         public FunctionDescriptor withVoidReturnLayout() {
241             throw new UnsupportedOperationException();
242         }
243 
244         @Override
245         public Optional<DynamicConstantDesc<FunctionDescriptor>> describeConstable() {
246             return Optional.empty();
247         }
248     }
249 }