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 package jdk.internal.foreign.abi;
 26 
 27 import jdk.incubator.foreign.FunctionDescriptor;
 28 import jdk.incubator.foreign.MemoryLayout;
 29 import sun.security.action.GetPropertyAction;
 30 
 31 import java.lang.invoke.MethodType;
 32 import java.util.ArrayDeque;
 33 import java.util.ArrayList;
 34 import java.util.Deque;
 35 import java.util.EnumSet;
 36 import java.util.List;
 37 import java.util.Set;
 38 
 39 import static jdk.internal.foreign.abi.Binding.Tag.*;
 40 
 41 public class CallingSequenceBuilder {
 42     private static final boolean VERIFY_BINDINGS = Boolean.parseBoolean(
 43             GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.VERIFY_BINDINGS", "true"));
 44 
 45     private boolean isTrivial;
 46     private final boolean forUpcall;
 47     private final List<List<Binding>> inputBindings = new ArrayList<>();
 48     private List<Binding> outputBindings = List.of();
 49 
 50     private MethodType mt = MethodType.methodType(void.class);
 51     private FunctionDescriptor desc = FunctionDescriptor.ofVoid();
 52 
 53     public CallingSequenceBuilder(boolean forUpcall) {
 54         this.forUpcall = forUpcall;
 55     }
 56 
 57     public final CallingSequenceBuilder addArgumentBindings(Class<?> carrier, MemoryLayout layout,
 58                                                             List<Binding> bindings) {
 59         verifyBindings(true, carrier, bindings);
 60         inputBindings.add(bindings);
 61         mt = mt.appendParameterTypes(carrier);
 62         desc = desc.withAppendedArgumentLayouts(layout);
 63         return this;
 64     }
 65 
 66     public CallingSequenceBuilder setReturnBindings(Class<?> carrier, MemoryLayout layout,
 67                                                     List<Binding> bindings) {
 68         verifyBindings(false, carrier, bindings);
 69         this.outputBindings = bindings;
 70         mt = mt.changeReturnType(carrier);
 71         desc = desc.withReturnLayout(layout);
 72         return this;
 73     }
 74 
 75     public CallingSequenceBuilder setTrivial(boolean isTrivial) {
 76         this.isTrivial = isTrivial;
 77         return this;
 78     }
 79 
 80     public CallingSequence build() {
 81         return new CallingSequence(mt, desc, isTrivial, inputBindings, outputBindings);
 82     }
 83 
 84     private void verifyBindings(boolean forArguments, Class<?> carrier, List<Binding> bindings) {
 85         if (VERIFY_BINDINGS) {
 86             if (forUpcall == forArguments) {
 87                 verifyBoxBindings(carrier, bindings);
 88             } else {
 89                 verifyUnboxBindings(carrier, bindings);
 90             }
 91         }
 92     }
 93 
 94     private static final Set<Binding.Tag> UNBOX_TAGS = EnumSet.of(
 95         VM_STORE,
 96         //VM_LOAD,
 97         //BUFFER_STORE,
 98         BUFFER_LOAD,
 99         COPY_BUFFER,
100         //ALLOC_BUFFER,
101         //BOX_ADDRESS,
102         UNBOX_ADDRESS,
103         //TO_SEGMENT,
104         DUP
105     );
106 
107     private static void verifyUnboxBindings(Class<?> inType, List<Binding> bindings) {
108         Deque<Class<?>> stack = new ArrayDeque<>();
109         stack.push(inType);
110 
111         for (Binding b : bindings) {
112             if (!UNBOX_TAGS.contains(b.tag()))
113                 throw new IllegalArgumentException("Unexpected operator: " + b);
114             b.verify(stack);
115         }
116 
117         if (!stack.isEmpty()) {
118             throw new IllegalArgumentException("Stack must be empty after recipe");
119         }
120     }
121 
122     private static final Set<Binding.Tag> BOX_TAGS = EnumSet.of(
123         //VM_STORE,
124         VM_LOAD,
125         BUFFER_STORE,
126         //BUFFER_LOAD,
127         COPY_BUFFER,
128         ALLOC_BUFFER,
129         BOX_ADDRESS,
130         //UNBOX_ADDRESS,
131         TO_SEGMENT,
132         DUP
133     );
134 
135     private static void verifyBoxBindings(Class<?> expectedOutType, List<Binding> bindings) {
136         Deque<Class<?>> stack = new ArrayDeque<>();
137 
138         for (Binding b : bindings) {
139             if (!BOX_TAGS.contains(b.tag()))
140                 throw new IllegalArgumentException("Unexpected operator: " + b);
141             b.verify(stack);
142         }
143 
144         if (stack.size() != 1) {
145             throw new IllegalArgumentException("Stack must contain exactly 1 value");
146         }
147 
148         Class<?> actualOutType = stack.pop();
149         SharedUtils.checkType(actualOutType, expectedOutType);
150     }
151 }