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