1 /*
  2  *  Copyright (c) 2021, 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  */
 26 package jdk.incubator.foreign;
 27 
 28 import jdk.internal.foreign.abi.SharedUtils;
 29 import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
 30 import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
 31 import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
 32 import jdk.internal.foreign.abi.x64.windows.WinVaList;
 33 import jdk.internal.reflect.CallerSensitive;
 34 import jdk.internal.reflect.Reflection;
 35 
 36 import java.util.Objects;
 37 import java.util.function.Consumer;
 38 
 39 /**
 40  * An interface that models a variable argument list, similar in functionality to a C {@code va_list}.
 41  * <p>
 42  * A variable argument list is a stateful cursor used to iterate over a set of arguments. A variable argument list
 43  * can be passed by reference e.g. to a {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handle}.
 44  * <p>
 45  * Per the C specification (see C standard 6.5.2.2 Function calls - item 6),
 46  * arguments to variadic calls are erased by way of 'default argument promotions',
 47  * which erases integral types by way of integer promotion (see C standard 6.3.1.1 - item 2),
 48  * and which erases all {@code float} arguments to {@code double}.
 49  * <p>
 50  * As such, this interface only supports reading {@code int}, {@code double},
 51  * and any other type that fits into a {@code long}.
 52  *
 53  * This class is not thread safe, and all accesses should occur within a single thread
 54  * (regardless of the scope associated with the variable arity list).
 55  *
 56  * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
 57  * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
 58  */
 59 sealed public interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList {
 60 
 61     /**
 62      * Reads the next value as an {@code int} and advances this variable argument list's position.
 63      *
 64      * @param layout the layout of the value to be read.
 65      * @return the {@code int} value read from this variable argument list.
 66      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
 67      * a thread other than the thread owning that scope.
 68      */
 69     int nextVarg(ValueLayout.OfInt layout);
 70 
 71     /**
 72      * Reads the next value as a {@code long} and advances this variable argument list's position.
 73      *
 74      * @param layout the layout of the value to be read.
 75      * @return the {@code long} value read from this variable argument list.
 76      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
 77      * a thread other than the thread owning that scope.
 78      */
 79     long nextVarg(ValueLayout.OfLong layout);
 80 
 81     /**
 82      * Reads the next value as a {@code double} and advances this variable argument list's position.
 83      *
 84      * @param layout the layout of the value
 85      * @return the {@code double} value read from this variable argument list.
 86      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
 87      * a thread other than the thread owning that scope.
 88      */
 89     double nextVarg(ValueLayout.OfDouble layout);
 90 
 91     /**
 92      * Reads the next value as a {@code MemoryAddress} and advances this variable argument list's position.
 93      *
 94      * @param layout the layout of the value to be read.
 95      * @return the {@code MemoryAddress} value read from this variable argument list.
 96      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
 97      * a thread other than the thread owning that scope.
 98      */
 99     MemoryAddress nextVarg(ValueLayout.OfAddress layout);
100 
101     /**
102      * Reads the next value as a {@code MemorySegment}, and advances this variable argument list's position.
103      * <p>
104      * The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}.
105      *
106      * @param layout the layout of the value to be read.
107      * @param allocator the allocator to be used to create a segment where the contents of the variable argument list
108      *                  will be copied.
109      * @return the {@code MemorySegment} value read from this variable argument list.
110      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
111      * a thread other than the thread owning that scope.
112      */
113     MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
114 
115     /**
116      * Skips a number of elements with the given memory layouts, and advances this variable argument list's position.
117      *
118      * @param layouts the layouts of the values to be skipped.
119      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
120      * a thread other than the thread owning that scope.
121      */
122     void skip(MemoryLayout... layouts);
123 
124     /**
125      * Returns the resource scope associated with this variable argument list.
126      * @return the resource scope associated with this variable argument list.
127      */
128     ResourceScope scope();
129 
130     /**
131      * Copies this variable argument list at its current position into a new variable argument list associated
132      * with the same scope as this variable argument list. using the segment provided allocator. Copying is useful to
133      * traverse the variable argument list elements, starting from the current position, without affecting the state
134      * of the original variable argument list, essentially allowing the elements to be traversed multiple times.
135      *
136      * @return a copy of this variable argument list.
137      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
138      * a thread other than the thread owning that scope.
139      */
140     VaList copy();
141 
142     /**
143      * Returns the memory address associated with this variable argument list.
144      * @throws UnsupportedOperationException if this variable argument list has been allocated using heap segments.
145      * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
146      * a thread other than the thread owning that scope.
147      * @return The memory address associated with this variable argument list.
148      */
149     @Override
150     MemoryAddress address();
151 
152     /**
153      * Constructs a new variable argument list from a memory address pointing to an existing variable argument list,
154      * with given resource scope.
155      * <p>
156      * This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
157      * Restricted methods are unsafe, and, if used incorrectly, their use might crash
158      * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
159      * restricted methods, and use safe and supported functionalities, where possible.
160      *
161      * @param address a memory address pointing to an existing variable argument list.
162      * @param scope the resource scope to be associated with the returned variable argument list.
163      * @return a new variable argument list backed by the memory region at {@code address}.
164      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
165      * than the thread owning {@code scope}.
166      * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
167      * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
168      * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
169      */
170     @CallerSensitive
171     static VaList ofAddress(MemoryAddress address, ResourceScope scope) {
172         Reflection.ensureNativeAccess(Reflection.getCallerClass());
173         Objects.requireNonNull(address);
174         Objects.requireNonNull(scope);
175         return SharedUtils.newVaListOfAddress(address, scope);
176     }
177 
178     /**
179      * Constructs a new variable argument list using a builder (see {@link Builder}), with a given resource scope.
180      * <p>
181      * If this method needs to allocate native memory, such memory will be managed by the given
182      * {@linkplain ResourceScope resource scope}, and will be released when the resource scope is {@linkplain ResourceScope#close closed}.
183      * <p>
184      * Note that when there are no elements added to the created va list,
185      * this method will return the same as {@link #empty()}.
186      *
187      * @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
188      *                of the underlying variable argument list.
189      * @param scope scope the scope to be associated with the new variable arity list.
190      * @return a new variable argument list.
191      * @throws IllegalStateException if the scope associated with {@code allocator} has been already closed,
192      * or if access occurs from a thread other than the thread owning that scope.
193      */
194     static VaList make(Consumer<Builder> actions, ResourceScope scope) {
195         Objects.requireNonNull(actions);
196         Objects.requireNonNull(scope);
197         return SharedUtils.newVaList(actions, scope);
198     }
199 
200     /**
201      * Returns an empty variable argument list, associated with the {@linkplain ResourceScope#globalScope() global}
202      * scope. The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException}
203      * on all operations, except for {@link #scope()}, {@link #copy()} and {@link #address()}.
204      * @return an empty variable argument list.
205      */
206     static VaList empty() {
207         return SharedUtils.emptyVaList();
208     }
209 
210     /**
211      * A builder interface used to construct a variable argument list.
212      *
213      * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
214      * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
215      */
216     sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder {
217 
218         /**
219          * Writes an {@code int} value to the variable argument list being constructed.
220          *
221          * @param layout the layout of the value to be written.
222          * @param value the {@code int} value to be written.
223          * @return this builder.
224          */
225         Builder addVarg(ValueLayout.OfInt layout, int value);
226 
227         /**
228          * Writes a {@code long} value to the variable argument list being constructed.
229          *
230          * @param layout the layout of the value to be written.
231          * @param value the {@code long} value to be written.
232          * @return this builder.
233          */
234         Builder addVarg(ValueLayout.OfLong layout, long value);
235 
236         /**
237          * Writes a {@code double} value to the variable argument list being constructed.
238          *
239          * @param layout the layout of the value to be written.
240          * @param value the {@code double} value to be written.
241          * @return this builder.
242          */
243         Builder addVarg(ValueLayout.OfDouble layout, double value);
244 
245         /**
246          * Writes an {@code Addressable} value to the variable argument list being constructed.
247          *
248          * @param layout the layout of the value to be written.
249          * @param value the {@code Addressable} value to be written.
250          * @return this builder.
251          */
252         Builder addVarg(ValueLayout.OfAddress layout, Addressable value);
253 
254         /**
255          * Writes a {@code MemorySegment} value, with given layout, to the variable argument list being constructed.
256          *
257          * @param layout the layout of the value to be written.
258          * @param value the {@code MemorySegment} whose contents will be copied.
259          * @return this builder.
260          */
261         Builder addVarg(GroupLayout layout, MemorySegment value);
262     }
263 }