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.foreign.MemoryAddress;
 29 import jdk.incubator.foreign.MemoryLayout;
 30 import jdk.incubator.foreign.ValueLayout;
 31 import jdk.incubator.jextract.Declaration;
 32 import jdk.incubator.jextract.Type;
 33 
 34 import javax.tools.JavaFileObject;
 35 import java.lang.constant.ClassDesc;
 36 import java.util.*;
 37 import java.util.function.Consumer;
 38 import java.util.stream.Collectors;
 39 
 40 /**
 41  * A helper class to generate header interface class in source form.
 42  * After aggregating various constituents of a .java source, build
 43  * method is called to get overall generated source string.
 44  */
 45 class ToplevelBuilder extends JavaSourceBuilder {
 46 
 47     private int declCount;
 48     private final List<JavaSourceBuilder> builders = new ArrayList<>();
 49     private SplitHeader lastHeader;
 50     private RootConstants rootConstants;
 51     private int headersCount;
 52     private final ClassDesc headerDesc;
 53 
 54     static final int DECLS_PER_HEADER_CLASS = Integer.getInteger("jextract.decls.per.header", 1000);
 55 
 56     ToplevelBuilder(String packageName, String headerClassName) {
 57         this.headerDesc = ClassDesc.of(packageName, headerClassName);
 58         SplitHeader first = lastHeader = new FirstHeader(headerClassName);
 59         rootConstants = new RootConstants();
 60         first.classBegin();
 61         builders.add(first);
 62     }
 63 
 64     public RootConstants rootConstants() {
 65         return rootConstants;
 66     }
 67 
 68     public List<JavaFileObject> toFiles() {
 69         if (constantBuilder != null) {
 70             constantBuilder.classEnd();
 71         }
 72         lastHeader.classEnd();
 73         builders.addAll(constantBuilders);
 74         builders.add(rootConstants);
 75         List<JavaFileObject> files = new ArrayList<>();
 76         files.addAll(builders.stream()
 77                 .flatMap(b -> b.toFiles().stream())
 78                 .collect(Collectors.toList()));
 79         return files;
 80     }
 81 
 82     public String headerClassName() {
 83         return headerDesc.displayName();
 84     }
 85 
 86     @Override
 87     boolean isEnclosedBySameName(String name) {
 88         return false;
 89     }
 90 
 91     @Override
 92     public String packageName() {
 93         return headerDesc.packageName();
 94     }
 95 
 96     @Override
 97     public void addVar(String javaName, String nativeName, VarInfo varInfo) {
 98         nextHeader().addVar(javaName, nativeName, varInfo);
 99     }
100 
101     @Override
102     public void addFunction(String javaName, String nativeName, FunctionInfo functionInfo) {
103         nextHeader().addFunction(javaName, nativeName, functionInfo);
104     }
105 
106     @Override
107     public void addConstant(String javaName, Class<?> type, Object value) {
108         nextHeader().addConstant(javaName, type, value);
109     }
110 
111     @Override
112     public void addTypedef(String name, String superClass, Type type) {
113         if (type instanceof Type.Primitive) {
114             // primitive
115             nextHeader().emitPrimitiveTypedef((Type.Primitive)type, name);
116         } else if (((TypeImpl)type).isPointer()) {
117             // pointer typedef
118             nextHeader().emitPointerTypedef(name);
119         } else {
120             TypedefBuilder builder = new TypedefBuilder(this, name, superClass);
121             builders.add(builder);
122             builder.classBegin();
123             builder.classEnd();
124         }
125     }
126 
127     @Override
128     public StructBuilder addStruct(String name, Declaration parent, GroupLayout layout, Type type) {
129         String structName = name.isEmpty() ? parent.name() : name;
130         StructBuilder structBuilder = new StructBuilder(this, structName, layout, type);
131         builders.add(structBuilder);
132         return structBuilder;
133     }
134 
135     @Override
136     public String addFunctionalInterface(String name, FunctionInfo functionInfo) {
137         FunctionalInterfaceBuilder builder = new FunctionalInterfaceBuilder(this, name, functionInfo);
138         builders.add(builder);
139         builder.classBegin();
140         builder.classEnd();
141         return builder.className();
142     }
143 
144     private SplitHeader nextHeader() {
145         if (declCount == DECLS_PER_HEADER_CLASS) {
146             boolean hasSuper = !(lastHeader instanceof FirstHeader);
147             SplitHeader headerFileBuilder = new SplitHeader(headerDesc.displayName() + "_" + ++headersCount,
148                     hasSuper ? lastHeader.className() : null);
149             lastHeader.classEnd();
150             headerFileBuilder.classBegin();
151             builders.add(headerFileBuilder);
152             lastHeader = headerFileBuilder;
153             declCount = 1;
154             return headerFileBuilder;
155         } else {
156             declCount++;
157             return lastHeader;
158         }
159     }
160 
161     class SplitHeader extends HeaderFileBuilder {
162         SplitHeader(String name, String superclass) {
163             super(ToplevelBuilder.this, name, superclass);
164         }
165 
166         @Override
167         String mods() {
168             return " ";
169         }
170     }
171 
172     class FirstHeader extends SplitHeader {
173 
174         FirstHeader(String name) {
175             super(name, "#{SUPER}");
176         }
177 
178         @Override
179         String mods() {
180             return "public ";
181         }
182 
183         @Override
184         void classBegin() {
185             super.classBegin();
186             emitConstructor();
187             // emit basic primitive types
188             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Bool), "C_BOOL");
189             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Char), "C_CHAR");
190             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Short), "C_SHORT");
191             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Int), "C_INT");
192             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Long), "C_LONG");
193             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.LongLong), "C_LONG_LONG");
194             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Float), "C_FLOAT");
195             emitPrimitiveTypedef(Type.primitive(Type.Primitive.Kind.Double), "C_DOUBLE");
196             emitPointerTypedef("C_POINTER");
197         }
198 
199         void emitConstructor() {
200             incrAlign();
201             indent();
202             append("/* package-private */ ");
203             append(className());
204             append("() {}");
205             append('\n');
206             decrAlign();
207         }
208 
209         @Override
210         String build() {
211             HeaderFileBuilder last = lastHeader;
212             return super.build().replace("extends #{SUPER}",
213                     last != this ? "extends " + last.className() : "");
214         }
215     }
216 
217     // constant support
218 
219     class RootConstants extends ConstantBuilder {
220 
221         private final Map<ValueLayout, Constant> primitiveLayouts = new HashMap<>();
222 
223         public RootConstants() {
224             super(ToplevelBuilder.this, "Constants$root");
225             classBegin();
226             addPrimitiveLayout("C_BOOL", Type.Primitive.Kind.Bool);
227             addPrimitiveLayout("C_CHAR", Type.Primitive.Kind.Char);
228             addPrimitiveLayout("C_SHORT", Type.Primitive.Kind.Short);
229             addPrimitiveLayout("C_INT", Type.Primitive.Kind.Int);
230             addPrimitiveLayout("C_LONG", Type.Primitive.Kind.Long);
231             addPrimitiveLayout("C_LONG_LONG", Type.Primitive.Kind.LongLong);
232             addPrimitiveLayout("C_FLOAT", Type.Primitive.Kind.Float);
233             addPrimitiveLayout("C_DOUBLE", Type.Primitive.Kind.Double);
234             addPrimitiveLayout("C_POINTER", TypeImpl.PointerImpl.POINTER_LAYOUT);
235             classEnd();
236         }
237 
238         @Override
239         protected String primitiveLayoutString(ValueLayout vl) {
240             if (vl.carrier() == boolean.class) {
241                 return "JAVA_BOOLEAN";
242             } else if (vl.carrier() == char.class) {
243                 return "JAVA_CHAR.withBitAlignment(" + vl.bitAlignment() + ")";
244             } else if (vl.carrier() == byte.class) {
245                 return "JAVA_BYTE";
246             } else if (vl.carrier() == short.class) {
247                 return "JAVA_SHORT.withBitAlignment(" + vl.bitAlignment() + ")";
248             } else if (vl.carrier() == int.class) {
249                 return "JAVA_INT.withBitAlignment(" + vl.bitAlignment() + ")";
250             } else if (vl.carrier() == float.class) {
251                 return "JAVA_FLOAT.withBitAlignment(" + vl.bitAlignment() + ")";
252             } else if (vl.carrier() == long.class) {
253                 return "JAVA_LONG.withBitAlignment(" + vl.bitAlignment() + ")";
254             } else if (vl.carrier() == double.class) {
255                 return "JAVA_DOUBLE.withBitAlignment(" + vl.bitAlignment() + ")";
256             } else if (vl.carrier() == MemoryAddress.class) {
257                 return "ADDRESS.withBitAlignment(" + vl.bitAlignment() + ")";
258             } else {
259                 return "MemoryLayout.paddingLayout(" + vl.bitSize() +  ")";
260             }
261         }
262 
263         private Constant addPrimitiveLayout(String javaName, ValueLayout layout) {
264             ValueLayout layoutNoName = layoutNoName(layout);
265             Constant layoutConstant = super.addLayout(javaName, layoutNoName);
266             primitiveLayouts.put(layoutNoName, layoutConstant);
267             return layoutConstant;
268         }
269 
270         private Constant addPrimitiveLayout(String javaName, Type.Primitive.Kind kind) {
271             return addPrimitiveLayout(javaName, (ValueLayout)kind.layout().get());
272         }
273 
274         private ValueLayout layoutNoName(ValueLayout layout) {
275             // drop name if present
276             return MemoryLayout.valueLayout(layout.carrier(), layout.order())
277                     .withBitAlignment(layout.bitAlignment());
278         }
279 
280         public Constant resolvePrimitiveLayout(ValueLayout layout) {
281             return primitiveLayouts.get(layoutNoName(layout));
282         }
283     }
284 
285     // other constants
286 
287     int constant_counter = 0;
288     int constant_class_index = 0;
289     List<ConstantBuilder> constantBuilders = new ArrayList<>();
290 
291     static final int CONSTANTS_PER_CLASS = Integer.getInteger("jextract.constants.per.class", 5);
292     ConstantBuilder constantBuilder;
293 
294     protected void emitWithConstantClass(Consumer<ConstantBuilder> constantConsumer) {
295         if (constant_counter > CONSTANTS_PER_CLASS || constantBuilder == null) {
296             if (constantBuilder != null) {
297                 constantBuilder.classEnd();
298             }
299             constant_counter = 0;
300             constantBuilder = new ConstantBuilder(this, "constants$" + constant_class_index++) {
301                 @Override
302                 String mods() {
303                     return ""; // constants package-private!
304                 }
305             };
306             constantBuilders.add(constantBuilder);
307             constantBuilder.classBegin();
308         }
309         constantConsumer.accept(constantBuilder);
310         constant_counter++;
311     }
312 }