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.jextract.impl;
 27 
 28 import jdk.incubator.foreign.*;
 29 
 30 import jdk.internal.jextract.impl.ConstantBuilder.Constant;
 31 
 32 import java.lang.invoke.MethodType;
 33 import java.util.List;
 34 import java.util.Optional;
 35 import java.util.function.Consumer;
 36 import java.util.stream.Collectors;
 37 import java.util.stream.IntStream;
 38 
 39 public class FunctionalInterfaceBuilder extends ClassSourceBuilder {
 40 
 41     private static final String MEMBER_MODS = "static";
 42 
 43     private final MethodType fiType;
 44     private final MethodType downcallType;
 45     private final FunctionDescriptor fiDesc;
 46     private final Optional<List<String>> parameterNames;
 47 
 48     FunctionalInterfaceBuilder(JavaSourceBuilder enclosing, String className,
 49                                FunctionInfo functionInfo) {
 50         super(enclosing, Kind.INTERFACE, className);
 51         this.fiType = functionInfo.methodType();
 52         this.downcallType = functionInfo.reverseMethodType();
 53         this.fiDesc = functionInfo.descriptor();
 54         this.parameterNames = functionInfo.parameterNames();
 55     }
 56 
 57     @Override
 58     JavaSourceBuilder classEnd() {
 59         emitFunctionalInterfaceMethod();
 60         emitFunctionalFactories();
 61         emitFunctionalFactoryForPointer();
 62         return super.classEnd();
 63     }
 64 
 65     // private generation
 66 
 67     private String parameterName(int i) {
 68         String name = "";
 69         if (parameterNames.isPresent()) {
 70             name = parameterNames.get().get(i);
 71         }
 72         return name.isEmpty()? "_x" + i : Utils.javaSafeIdentifier(name);
 73     }
 74 
 75     private void emitFunctionalInterfaceMethod() {
 76         incrAlign();
 77         indent();
 78         append(fiType.returnType().getName() + " apply(");
 79         String delim = "";
 80         for (int i = 0 ; i < fiType.parameterCount(); i++) {
 81             append(delim + fiType.parameterType(i).getName());
 82             append(" ");
 83             append(parameterName(i));
 84             delim = ", ";
 85         }
 86         append(");\n");
 87         decrAlign();
 88     }
 89 
 90     private void emitFunctionalFactories() {
 91         emitWithConstantClass(constantBuilder -> {
 92             Constant functionDesc = constantBuilder.addFunctionDesc(className(), fiDesc);
 93             incrAlign();
 94             indent();
 95             append(MEMBER_MODS + " NativeSymbol allocate(" + className() + " fi, ResourceScope scope) {\n");
 96             incrAlign();
 97             indent();
 98             append("return RuntimeHelper.upcallStub(" + className() + ".class, fi, " + functionDesc.accessExpression() + ", " +
 99                     "\"" + fiType.toMethodDescriptorString() + "\", scope);\n");
100             decrAlign();
101             indent();
102             append("}\n");
103             decrAlign();
104         });
105     }
106 
107     private void emitFunctionalFactoryForPointer() {
108         emitWithConstantClass(constantBuilder -> {
109             Constant mhConstant = constantBuilder.addMethodHandle(className(), className(),
110                  FunctionInfo.ofFunctionPointer(downcallType, fiType, fiDesc, parameterNames), true);
111             incrAlign();
112             indent();
113             append(MEMBER_MODS + " " + className() + " ofAddress(MemoryAddress addr, ResourceScope scope) {\n");
114             incrAlign();
115             indent();
116             append("NativeSymbol symbol = NativeSymbol.ofAddress(");
117             append("\"" + className() + "::\" + Long.toHexString(addr.toRawLongValue()), addr, scope);\n");
118             append("return (");
119             String delim = "";
120             for (int i = 0 ; i < fiType.parameterCount(); i++) {
121                 append(delim + fiType.parameterType(i).getName());
122                 append(" ");
123                 append(parameterName(i));
124                 delim = ", ";
125             }
126             append(") -> {\n");
127             incrAlign();
128             indent();
129             append("try {\n");
130             incrAlign();
131             indent();
132             if (!fiType.returnType().equals(void.class)) {
133                 append("return (" + fiType.returnType().getName() + ")");
134                 if (fiType.returnType() != downcallType.returnType()) {
135                     // add cast for invokeExact
136                     append("(" + downcallType.returnType().getName() + ")");
137                 }
138             }
139             append(mhConstant.accessExpression() + ".invokeExact(symbol");
140             if (fiType.parameterCount() > 0) {
141                 String params = IntStream.range(0, fiType.parameterCount())
142                         .mapToObj(i -> {
143                             String paramExpr = parameterName(i);
144                             if (fiType.parameterType(i) != downcallType.parameterType(i)) {
145                                 // add cast for invokeExact
146                                 return "(" + downcallType.parameterType(i).getName() + ")" + paramExpr;
147                             } else {
148                                 return paramExpr;
149                             }
150                         })
151                         .collect(Collectors.joining(", "));
152                 append(", " + params);
153             }
154             append(");\n");
155             decrAlign();
156             indent();
157             append("} catch (Throwable ex$) {\n");
158             incrAlign();
159             indent();
160             append("throw new AssertionError(\"should not reach here\", ex$);\n");
161             decrAlign();
162             indent();
163             append("}\n");
164             decrAlign();
165             indent();
166             append("};\n");
167             decrAlign();
168             indent();
169             append("}\n");
170             decrAlign();
171         });
172     }
173 
174     @Override
175     protected void emitWithConstantClass(Consumer<ConstantBuilder> constantConsumer) {
176         enclosing.emitWithConstantClass(constantConsumer);
177     }
178 }