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  */
 26 package jdk.internal.foreign.abi.x64.windows;
 27 
 28 import jdk.incubator.foreign.*;
 29 import jdk.internal.foreign.Scoped;
 30 import jdk.internal.foreign.ResourceScopeImpl;
 31 import jdk.internal.foreign.abi.SharedUtils;
 32 import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
 33 
 34 import java.lang.invoke.VarHandle;
 35 import java.util.ArrayList;
 36 import java.util.List;
 37 import java.util.Objects;
 38 import java.util.stream.Stream;
 39 
 40 import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER;
 41 
 42 // see vadefs.h (VC header)
 43 //
 44 // in short
 45 // -> va_list is just a pointer to a buffer with 64 bit entries.
 46 // -> non-power-of-two-sized, or larger than 64 bit types passed by reference.
 47 // -> other types passed in 64 bit slots by normal function calling convention.
 48 //
 49 // X64 va_arg impl:
 50 //
 51 //    typedef char* va_list;
 52 //
 53 //    #define __crt_va_arg(ap, t)                                               \
 54 //        ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \
 55 //            ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64))             \
 56 //            :  *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
 57 //
 58 public non-sealed class WinVaList implements VaList, Scoped {
 59     public static final Class<?> CARRIER = MemoryAddress.class;
 60     private static final long VA_SLOT_SIZE_BYTES = 8;
 61     private static final VarHandle VH_address = C_POINTER.varHandle();
 62 
 63     private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL);
 64 
 65     private MemorySegment segment;
 66     private final ResourceScope scope;
 67 
 68     private WinVaList(MemorySegment segment, ResourceScope scope) {
 69         this.segment = segment;
 70         this.scope = scope;
 71     }
 72 
 73     public static final VaList empty() {
 74         return EMPTY;
 75     }
 76 
 77     @Override
 78     public int nextVarg(ValueLayout.OfInt layout) {
 79         return (int) read(int.class, layout);
 80     }
 81 
 82     @Override
 83     public long nextVarg(ValueLayout.OfLong layout) {
 84         return (long) read(long.class, layout);
 85     }
 86 
 87     @Override
 88     public double nextVarg(ValueLayout.OfDouble layout) {
 89         return (double) read(double.class, layout);
 90     }
 91 
 92     @Override
 93     public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
 94         return (MemoryAddress) read(MemoryAddress.class, layout);
 95     }
 96 
 97     @Override
 98     public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
 99         Objects.requireNonNull(allocator);
100         return (MemorySegment) read(MemorySegment.class, layout, allocator);
101     }
102 
103     private Object read(Class<?> carrier, MemoryLayout layout) {
104         return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
105     }
106 
107     private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
108         Objects.requireNonNull(layout);
109         Object res;
110         if (carrier == MemorySegment.class) {
111             TypeClass typeClass = TypeClass.typeClassFor(layout, false);
112             res = switch (typeClass) {
113                 case STRUCT_REFERENCE -> {
114                     MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment);
115                     MemorySegment struct = MemorySegment.ofAddressNative(structAddr, layout.byteSize(), scope());
116                     MemorySegment seg = allocator.allocate(layout);
117                     seg.copyFrom(struct);
118                     yield seg;
119                 }
120                 case STRUCT_REGISTER ->
121                     allocator.allocate(layout).copyFrom(segment.asSlice(0, layout.byteSize()));
122                 default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
123             };
124         } else {
125             VarHandle reader = layout.varHandle();
126             res = reader.get(segment);
127         }
128         segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
129         return res;
130     }
131 
132     @Override
133     public void skip(MemoryLayout... layouts) {
134         Objects.requireNonNull(layouts);
135         ((ResourceScopeImpl)scope).checkValidStateSlow();
136         Stream.of(layouts).forEach(Objects::requireNonNull);
137         segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES);
138     }
139 
140     static WinVaList ofAddress(MemoryAddress addr, ResourceScope scope) {
141         MemorySegment segment = MemorySegment.ofAddressNative(addr, Long.MAX_VALUE, scope);
142         return new WinVaList(segment, scope);
143     }
144 
145     static Builder builder(ResourceScope scope) {
146         return new Builder(scope);
147     }
148 
149     @Override
150     public ResourceScope scope() {
151         return scope;
152     }
153 
154     @Override
155     public VaList copy() {
156         ((ResourceScopeImpl)scope).checkValidStateSlow();
157         return new WinVaList(segment, scope);
158     }
159 
160     @Override
161     public MemoryAddress address() {
162         return segment.address();
163     }
164 
165     public static non-sealed class Builder implements VaList.Builder {
166 
167         private final ResourceScope scope;
168         private final List<SimpleVaArg> args = new ArrayList<>();
169 
170         public Builder(ResourceScope scope) {
171             ((ResourceScopeImpl)scope).checkValidStateSlow();
172             this.scope = scope;
173         }
174 
175         private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
176             Objects.requireNonNull(layout);
177             Objects.requireNonNull(value);
178             args.add(new SimpleVaArg(carrier, layout, value));
179             return this;
180         }
181 
182         @Override
183         public Builder addVarg(ValueLayout.OfInt layout, int value) {
184             return arg(int.class, layout, value);
185         }
186 
187         @Override
188         public Builder addVarg(ValueLayout.OfLong layout, long value) {
189             return arg(long.class, layout, value);
190         }
191 
192         @Override
193         public Builder addVarg(ValueLayout.OfDouble layout, double value) {
194             return arg(double.class, layout, value);
195         }
196 
197         @Override
198         public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
199             return arg(MemoryAddress.class, layout, value.address());
200         }
201 
202         @Override
203         public Builder addVarg(GroupLayout layout, MemorySegment value) {
204             return arg(MemorySegment.class, layout, value);
205         }
206 
207         public VaList build() {
208             if (args.isEmpty()) {
209                 return EMPTY;
210             }
211             SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
212             MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size());
213             List<MemorySegment> attachedSegments = new ArrayList<>();
214             attachedSegments.add(segment);
215             MemorySegment cursor = segment;
216 
217             for (SimpleVaArg arg : args) {
218                 if (arg.carrier == MemorySegment.class) {
219                     MemorySegment msArg = ((MemorySegment) arg.value);
220                     TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
221                     switch (typeClass) {
222                         case STRUCT_REFERENCE -> {
223                             MemorySegment copy = allocator.allocate(arg.layout);
224                             copy.copyFrom(msArg); // by-value
225                             attachedSegments.add(copy);
226                             VH_address.set(cursor, copy.address());
227                         }
228                         case STRUCT_REGISTER ->
229                             cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES));
230                         default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
231                     }
232                 } else {
233                     VarHandle writer = arg.varHandle();
234                     writer.set(cursor, arg.value);
235                 }
236                 cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
237             }
238 
239             return new WinVaList(segment, scope);
240         }
241     }
242 }