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