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.incubator.foreign.CLinker.VaList;
 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 {
 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 = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class));
 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 vargAsInt(MemoryLayout layout) {
 79         return (int) read(int.class, layout);
 80     }
 81 
 82     @Override
 83     public long vargAsLong(MemoryLayout layout) {
 84         return (long) read(long.class, layout);
 85     }
 86 
 87     @Override
 88     public double vargAsDouble(MemoryLayout layout) {
 89         return (double) read(double.class, layout);
 90     }
 91 
 92     @Override
 93     public MemoryAddress vargAsAddress(MemoryLayout layout) {
 94         return (MemoryAddress) read(MemoryAddress.class, layout);
 95     }
 96 
 97     @Override
 98     public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) {
 99         Objects.requireNonNull(allocator);
100         return (MemorySegment) read(MemorySegment.class, layout, allocator);
101     }
102 
103     @Override
104     public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
105         return vargAsSegment(layout, SegmentAllocator.ofScope(scope));
106     }
107 
108     private Object read(Class<?> carrier, MemoryLayout layout) {
109         return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
110     }
111 
112     private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
113         Objects.requireNonNull(layout);
114         SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE);
115         Object res;
116         if (carrier == MemorySegment.class) {
117             TypeClass typeClass = TypeClass.typeClassFor(layout);
118             res = switch (typeClass) {
119                 case STRUCT_REFERENCE -> {
120                     MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment);
121                     MemorySegment struct = structAddr.asSegment(layout.byteSize(), scope());
122                     MemorySegment seg = allocator.allocate(layout);
123                     seg.copyFrom(struct);
124                     yield seg;
125                 }
126                 case STRUCT_REGISTER -> {
127                     MemorySegment struct = allocator.allocate(layout);
128                     struct.copyFrom(segment.asSlice(0L, layout.byteSize()));
129                     yield struct;
130                 }
131                 default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
132             };
133         } else {
134             VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout);
135             res = reader.get(segment);
136         }
137         segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
138         return res;
139     }
140 
141     @Override
142     public void skip(MemoryLayout... layouts) {
143         Objects.requireNonNull(layouts);
144         Stream.of(layouts).forEach(Objects::requireNonNull);
145         segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES);
146     }
147 
148     static WinVaList ofAddress(MemoryAddress addr, ResourceScope scope) {
149         MemorySegment segment = addr.asSegment(Long.MAX_VALUE, scope);
150         return new WinVaList(segment, scope);
151     }
152 
153     static Builder builder(ResourceScope scope) {
154         return new Builder(scope);
155     }
156 
157     @Override
158     public ResourceScope scope() {
159         return scope;
160     }
161 
162     @Override
163     public VaList copy() {
164         ((ResourceScopeImpl)scope).checkValidStateSlow();
165         return new WinVaList(segment, scope);
166     }
167 
168     @Override
169     public MemoryAddress address() {
170         return segment.address();
171     }
172 
173     public static non-sealed class Builder implements VaList.Builder {
174 
175         private final ResourceScope scope;
176         private final List<SimpleVaArg> args = new ArrayList<>();
177 
178         public Builder(ResourceScope scope) {
179             ((ResourceScopeImpl)scope).checkValidStateSlow();
180             this.scope = scope;
181         }
182 
183         private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
184             Objects.requireNonNull(layout);
185             Objects.requireNonNull(value);
186             SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE);
187             args.add(new SimpleVaArg(carrier, layout, value));
188             return this;
189         }
190 
191         @Override
192         public Builder vargFromInt(ValueLayout layout, int value) {
193             return arg(int.class, layout, value);
194         }
195 
196         @Override
197         public Builder vargFromLong(ValueLayout layout, long value) {
198             return arg(long.class, layout, value);
199         }
200 
201         @Override
202         public Builder vargFromDouble(ValueLayout layout, double value) {
203             return arg(double.class, layout, value);
204         }
205 
206         @Override
207         public Builder vargFromAddress(ValueLayout layout, Addressable value) {
208             return arg(MemoryAddress.class, layout, value.address());
209         }
210 
211         @Override
212         public Builder vargFromSegment(GroupLayout layout, MemorySegment value) {
213             return arg(MemorySegment.class, layout, value);
214         }
215 
216         public VaList build() {
217             if (args.isEmpty()) {
218                 return EMPTY;
219             }
220             SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope);
221             MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size());
222             List<MemorySegment> attachedSegments = new ArrayList<>();
223             attachedSegments.add(segment);
224             MemorySegment cursor = segment;
225 
226             for (SimpleVaArg arg : args) {
227                 if (arg.carrier == MemorySegment.class) {
228                     MemorySegment msArg = ((MemorySegment) arg.value);
229                     TypeClass typeClass = TypeClass.typeClassFor(arg.layout);
230                     switch (typeClass) {
231                         case STRUCT_REFERENCE -> {
232                             MemorySegment copy = allocator.allocate(arg.layout);
233                             copy.copyFrom(msArg); // by-value
234                             attachedSegments.add(copy);
235                             VH_address.set(cursor, copy.address());
236                         }
237                         case STRUCT_REGISTER -> {
238                             MemorySegment slice = cursor.asSlice(0, VA_SLOT_SIZE_BYTES);
239                             slice.copyFrom(msArg);
240                         }
241                         default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
242                     }
243                 } else {
244                     VarHandle writer = arg.varHandle();
245                     writer.set(cursor, arg.value);
246                 }
247                 cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
248             }
249 
250             return new WinVaList(segment, scope);
251         }
252     }
253 }