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