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.jextract.impl;
 26 
 27 import jdk.incubator.foreign.GroupLayout;
 28 import jdk.incubator.jextract.Declaration;
 29 import jdk.incubator.jextract.Type;
 30 
 31 import javax.tools.JavaFileObject;
 32 import java.io.File;
 33 import java.lang.constant.ClassDesc;
 34 import java.util.*;
 35 import java.util.function.Consumer;
 36 import java.util.stream.Collectors;
 37 
 38 /**
 39  * A helper class to generate header interface class in source form.
 40  * After aggregating various constituents of a .java source, build
 41  * method is called to get overall generated source string.
 42  */
 43 class ToplevelBuilder extends JavaSourceBuilder {
 44 
 45     private int declCount;
 46     private final List<JavaSourceBuilder> builders = new ArrayList<>();
 47     private SplitHeader lastHeader;
 48     private int headersCount;
 49     private final ClassDesc headerDesc;
 50 
 51     static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000);
 52 
 53     ToplevelBuilder(String packageName, String headerClassName) {
 54         this.headerDesc = ClassDesc.of(packageName, headerClassName);
 55         SplitHeader first = lastHeader = new FirstHeader(headerClassName);
 56         first.classBegin();
 57         builders.add(first);
 58     }
 59 
 60     public List<JavaFileObject> toFiles() {
 61         if (constantBuilder != null) {
 62             constantBuilder.classEnd();
 63         }
 64         lastHeader.classEnd();
 65         builders.addAll(constantBuilders);
 66         List<JavaFileObject> files = new ArrayList<>();
 67         files.addAll(builders.stream()
 68                 .flatMap(b -> b.toFiles().stream())
 69                 .collect(Collectors.toList()));
 70         return files;
 71     }
 72 
 73     public String headerClassName() {
 74         return headerDesc.displayName();
 75     }
 76 
 77     @Override
 78     boolean isEnclosedBySameName(String name) {
 79         return false;
 80     }
 81 
 82     @Override
 83     public String packageName() {
 84         return headerDesc.packageName();
 85     }
 86 
 87     @Override
 88     public void addVar(String javaName, String nativeName, VarInfo varInfo) {
 89         nextHeader().addVar(javaName, nativeName, varInfo);
 90     }
 91 
 92     @Override
 93     public void addFunction(String javaName, String nativeName, FunctionInfo functionInfo) {
 94         nextHeader().addFunction(javaName, nativeName, functionInfo);
 95     }
 96 
 97     @Override
 98     public void addConstant(String javaName, Class<?> type, Object value) {
 99         nextHeader().addConstant(javaName, type, value);
100     }
101 
102     @Override
103     public void addTypedef(String name, String superClass, Type type) {
104         if (type instanceof Type.Primitive) {
105             // primitive
106             nextHeader().emitPrimitiveTypedef((Type.Primitive)type, name);
107         } else if (((TypeImpl)type).isPointer()) {
108             // pointer typedef
109             nextHeader().emitPointerTypedef(name);
110         } else {
111             TypedefBuilder builder = new TypedefBuilder(this, name, superClass);
112             builders.add(builder);
113             builder.classBegin();
114             builder.classEnd();
115         }
116     }
117 
118     @Override
119     public StructBuilder addStruct(String name, Declaration parent, GroupLayout layout, Type type) {
120         String structName = name.isEmpty() ? parent.name() : name;
121         StructBuilder structBuilder = new StructBuilder(this, structName, layout, type);
122         builders.add(structBuilder);
123         return structBuilder;
124     }
125 
126     @Override
127     public String addFunctionalInterface(String name, FunctionInfo functionInfo) {
128         FunctionalInterfaceBuilder builder = new FunctionalInterfaceBuilder(this,
129                 name, functionInfo.methodType(), functionInfo.descriptor());
130         builders.add(builder);
131         builder.classBegin();
132         builder.classEnd();
133         return builder.className();
134     }
135 
136     private SplitHeader nextHeader() {
137         if (declCount == DECLS_PER_HEADER_CLASS) {
138             boolean hasSuper = !(lastHeader instanceof FirstHeader);
139             SplitHeader headerFileBuilder = new SplitHeader(headerDesc.displayName() + "_" + ++headersCount,
140                     hasSuper ? lastHeader.className() : null);
141             lastHeader.classEnd();
142             headerFileBuilder.classBegin();
143             builders.add(headerFileBuilder);
144             lastHeader = headerFileBuilder;
145             declCount = 1;
146             return headerFileBuilder;
147         } else {
148             declCount++;
149             return lastHeader;
150         }
151     }
152 
153     class SplitHeader extends HeaderFileBuilder {
154         SplitHeader(String name, String superclass) {
155             super(ToplevelBuilder.this, name, superclass);
156         }
157 
158         @Override
159         String mods() {
160             return " ";
161         }
162     }
163 
164     class FirstHeader extends SplitHeader {
165 
166         FirstHeader(String name) {
167             super(name, "#{SUPER}");
168         }
169 
170         @Override
171         String mods() {
172             return "public ";
173         }
174 
175         @Override
176         void classBegin() {
177             super.classBegin();
178             emitConstructor();
179             // emit basic primitive types
180             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Bool), "C_BOOL");
181             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Char), "C_CHAR");
182             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Short), "C_SHORT");
183             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Int), "C_INT");
184             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Long), "C_LONG");
185             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.LongLong), "C_LONG_LONG");
186             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Float), "C_FLOAT");
187             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Double), "C_DOUBLE");
188             emitPointerTypedef("C_POINTER");
189         }
190 
191         void emitConstructor() {
192             incrAlign();
193             indent();
194             append("/* package-private */ ");
195             append(className());
196             append("() {}");
197             append('\n');
198             decrAlign();
199         }
200 
201         @Override
202         String build() {
203             HeaderFileBuilder last = lastHeader;
204             return super.build().replace("extends #{SUPER}",
205                     last != this ? "extends " + last.className() : "");
206         }
207     }
208 
209     // constant support
210 
211     int constant_counter = 0;
212     int constant_class_index = 0;
213     List<ConstantBuilder> constantBuilders = new ArrayList<>();
214 
215     static final int CONSTANTS_PER_CLASS = Integer.getInteger("jextract.constants.per.class", 5);
216     ConstantBuilder constantBuilder;
217 
218     protected void emitWithConstantClass(Consumer<ConstantBuilder> constantConsumer) {
219         if (constant_counter > CONSTANTS_PER_CLASS || constantBuilder == null) {
220             if (constantBuilder != null) {
221                 constantBuilder.classEnd();
222             }
223             constant_counter = 0;
224             constantBuilder = new ConstantBuilder(this, "constants$" + constant_class_index++) {
225                 @Override
226                 String mods() {
227                     return ""; // constants package-private!
228                 }
229             };
230             constantBuilders.add(constantBuilder);
231             constantBuilder.classBegin();
232         }
233         constantConsumer.accept(constantBuilder);
234         constant_counter++;
235     }
236 }