1 /* 2 * Copyright (c) 2019, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jdk.internal.foreign.abi; 24 25 import jdk.incubator.foreign.FunctionDescriptor; 26 import jdk.incubator.foreign.MemoryLayout; 27 import sun.security.action.GetPropertyAction; 28 29 import java.lang.invoke.MethodType; 30 import java.util.ArrayDeque; 31 import java.util.ArrayList; 32 import java.util.Deque; 33 import java.util.List; 34 35 public class CallingSequenceBuilder { 36 private static final boolean VERIFY_BINDINGS = Boolean.parseBoolean( 37 GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.VERIFY_BINDINGS", "true")); 38 39 private final boolean forUpcall; 40 private final List<List<Binding>> inputBindings = new ArrayList<>(); 41 private List<Binding> outputBindings = List.of(); 42 43 private MethodType mt = MethodType.methodType(void.class); 44 private FunctionDescriptor desc = FunctionDescriptor.ofVoid(); 45 46 public CallingSequenceBuilder(boolean forUpcall) { 47 this.forUpcall = forUpcall; 48 } 49 50 public final CallingSequenceBuilder addArgumentBindings(Class<?> carrier, MemoryLayout layout, 51 List<Binding> bindings) { 52 verifyBindings(true, carrier, bindings); 53 inputBindings.add(bindings); 54 mt = mt.appendParameterTypes(carrier); 55 desc = desc.appendArgumentLayouts(layout); 56 return this; 57 } 58 59 public CallingSequenceBuilder setReturnBindings(Class<?> carrier, MemoryLayout layout, 60 List<Binding> bindings) { 61 verifyBindings(false, carrier, bindings); 62 this.outputBindings = bindings; 63 mt = mt.changeReturnType(carrier); 64 desc = desc.changeReturnLayout(layout); 65 return this; 66 } 67 68 public CallingSequence build() { 69 return new CallingSequence(mt, desc, inputBindings, outputBindings); 70 } 71 72 private void verifyBindings(boolean forArguments, Class<?> carrier, List<Binding> bindings) { 73 if (VERIFY_BINDINGS) { 74 if (forUpcall == forArguments) { 75 verifyBoxBindings(carrier, bindings); 76 } else { 77 verifyUnboxBindings(carrier, bindings); 78 } 79 } 80 } 81 82 private static void verifyUnboxBindings(Class<?> inType, List<Binding> bindings) { 83 Deque<Class<?>> stack = new ArrayDeque<>(); 84 stack.push(inType); 85 86 for (Binding b : bindings) { 87 b.verifyUnbox(stack); 88 } 89 90 if (!stack.isEmpty()) { 91 throw new IllegalArgumentException("Stack must be empty after recipe"); 92 } 93 } 94 95 private static void verifyBoxBindings(Class<?> expectedReturnType, List<Binding> bindings) { 96 Deque<Class<?>> stack = new ArrayDeque<>(); 97 98 for (Binding b : bindings) { 99 b.verifyBox(stack); 100 } 101 102 if (stack.size() != 1) { 103 throw new IllegalArgumentException("Stack must contain exactly 1 value"); 104 } 105 106 Class<?> actualReturnType = stack.pop(); 107 SharedUtils.checkType(actualReturnType, expectedReturnType); 108 } 109 }