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 java.lang.constant.Constable;
 29 import java.nio.file.Path;
 30 import java.util.ArrayList;
 31 import java.util.List;
 32 import java.util.Map;
 33 import java.util.Objects;
 34 import java.util.stream.Collectors;
 35 
 36 import jdk.incubator.foreign.MemoryLayout;
 37 import jdk.incubator.jextract.Declaration;
 38 import jdk.incubator.jextract.Position;
 39 import jdk.incubator.jextract.Type;
 40 import jdk.internal.clang.Cursor;
 41 import jdk.internal.clang.CursorKind;
 42 import jdk.internal.clang.CursorLanguage;
 43 import jdk.internal.clang.SourceLocation;
 44 
 45 class TreeMaker {
 46     public TreeMaker() {}
 47 
 48     TypeMaker typeMaker = new TypeMaker(this);
 49 
 50     public void freeze() {
 51         typeMaker.resolveTypeReferences();
 52     }
 53 
 54     interface ScopedFactoryLayout {
 55         Declaration.Scoped make(Position pos, String name, MemoryLayout layout, Declaration... decls);
 56     }
 57 
 58     interface ScopedFactoryNoLayout {
 59         Declaration.Scoped make(Position pos, String name, Declaration... decls);
 60     }
 61 
 62     interface VarFactoryNoLayout {
 63         Declaration.Variable make(Position pos, String name, Type type);
 64     }
 65 
 66     Map<String, List<Constable>> collectAttributes(Cursor c) {
 67         return c.children().filter(Cursor::isAttribute)
 68                 .collect(Collectors.groupingBy(
 69                         attr -> attr.kind().name(),
 70                         Collectors.mapping(Cursor::spelling, Collectors.toList())
 71                 ));
 72     }
 73 
 74     public Declaration createTree(Cursor c) {
 75         Objects.requireNonNull(c);
 76         CursorLanguage lang = c.language();
 77         if (lang != CursorLanguage.C && lang != CursorLanguage.Invalid) {
 78             throw new RuntimeException("Unsupported language: " + c.language());
 79         }
 80         var rv = (DeclarationImpl) createTreeInternal(c);
 81         return (rv == null) ? null : rv.withAttributes(collectAttributes(c));
 82     }
 83 
 84     private Declaration createTreeInternal(Cursor c) {
 85         switch (c.kind()) {
 86             case EnumDecl:
 87                 return createEnum(c, Declaration::enum_, Declaration::enum_);
 88             case EnumConstantDecl:
 89                 return createEnumConstant(c);
 90             case FieldDecl:
 91                 return createVar(c.isBitField() ?
 92                         Declaration.Variable.Kind.BITFIELD : Declaration.Variable.Kind.FIELD, c, Declaration::field);
 93             case ParmDecl:
 94                 return createVar(Declaration.Variable.Kind.PARAMETER, c, Declaration::parameter);
 95             case FunctionDecl:
 96                 return createFunction(c);
 97             case StructDecl:
 98                 return createRecord(c, Declaration.Scoped.Kind.STRUCT, Declaration::struct, Declaration::struct);
 99             case UnionDecl:
100                 return createRecord(c, Declaration.Scoped.Kind.UNION, Declaration::union, Declaration::union);
101             case TypedefDecl: {
102                 return createTypedef(c);
103             }
104             case VarDecl:
105                 return createVar(Declaration.Variable.Kind.GLOBAL, c, Declaration::globalVariable);
106             default:
107                 return null;
108         }
109     }
110 
111     static class CursorPosition implements Position {
112         private final Cursor cursor;
113         private final Path path;
114         private final int line;
115         private final int column;
116 
117         private CursorPosition(Cursor cursor) {
118             this.cursor = cursor;
119             SourceLocation.Location loc = cursor.getSourceLocation().getFileLocation();
120             this.path = loc.path();
121             this.line = loc.line();
122             this.column = loc.column();
123         }
124 
125         static Position of(Cursor cursor) {
126             SourceLocation loc = cursor.getSourceLocation();
127             if (loc == null) {
128                 return NO_POSITION;
129             }
130             SourceLocation.Location sloc = loc.getFileLocation();
131             if (sloc == null) {
132                 return NO_POSITION;
133             }
134             return new CursorPosition(cursor);
135         }
136 
137         @Override
138         public Path path() {
139             return path;
140         }
141 
142         @Override
143         public int line() {
144             return line;
145         }
146 
147         @Override
148         public int col() {
149             return column;
150         }
151 
152         public Cursor cursor() {
153             return cursor;
154         }
155 
156         @Override
157         public String toString() {
158             return PrettyPrinter.position(this);
159         }
160     }
161 
162     public Declaration.Function createFunction(Cursor c) {
163         checkCursor(c, CursorKind.FunctionDecl);
164         List<Declaration.Variable> params = new ArrayList<>();
165         for (int i = 0 ; i < c.numberOfArgs() ; i++) {
166             params.add((Declaration.Variable)createTree(c.getArgument(i)));
167         }
168         Type type = toType(c);
169         Type funcType = canonicalType(type);
170         return Declaration.function(CursorPosition.of(c), c.spelling(), (Type.Function)funcType,
171                 params.toArray(new Declaration.Variable[0]));
172     }
173 
174     public Declaration.Constant createMacro(Cursor c, String name, Type type, Object value) {
175         checkCursorAny(c, CursorKind.MacroDefinition);
176         return Declaration.constant(CursorPosition.of(c), name, value, type);
177     }
178 
179     public Declaration.Constant createEnumConstant(Cursor c) {
180         return Declaration.constant(CursorPosition.of(c), c.spelling(), c.getEnumConstantValue(), typeMaker.makeType(c.type()));
181     }
182 
183     public Declaration.Scoped createHeader(Cursor c, List<Declaration> decls) {
184         return Declaration.toplevel(CursorPosition.of(c), filterNestedDeclarations(decls).toArray(new Declaration[0]));
185     }
186 
187     public Declaration.Scoped createRecord(Cursor c, Declaration.Scoped.Kind scopeKind, ScopedFactoryLayout factoryLayout, ScopedFactoryNoLayout factoryNoLayout) {
188         Type.Declared t = (Type.Declared)RecordLayoutComputer.compute(typeMaker, 0, c.type(), c.type());
189         List<Declaration> decls = filterNestedDeclarations(t.tree().members());
190         if (c.isDefinition()) {
191             //just a declaration AND definition, we have a layout
192             return factoryLayout.make(CursorPosition.of(c), c.spelling(), t.tree().layout().get(), decls.toArray(new Declaration[0]));
193         } else {
194             //just a declaration
195             if (scopeKind == Declaration.Scoped.Kind.STRUCT ||
196                     scopeKind == Declaration.Scoped.Kind.UNION ||
197                     scopeKind == Declaration.Scoped.Kind.CLASS) {
198                 //if there's a real definition somewhere else, skip this redundant declaration
199                 if (!c.getDefinition().isInvalid()) {
200                     return null;
201                 }
202             }
203             return factoryNoLayout.make(CursorPosition.of(c), c.spelling(), decls.toArray(new Declaration[0]));
204         }
205     }
206 
207     public Declaration.Scoped createEnum(Cursor c, ScopedFactoryLayout factoryLayout, ScopedFactoryNoLayout factoryNoLayout) {
208         List<Declaration> decls = filterNestedDeclarations(c.children()
209                 .filter(fc -> {
210                     if (fc.isBitField()) {
211                         // only non-empty and named bit fields are generated
212                         return fc.getBitFieldWidth() != 0 && !fc.spelling().isEmpty();
213                     }
214                     return true;
215                 })
216                 .map(this::createTree).collect(Collectors.toList()));
217         if (c.isDefinition()) {
218             //just a declaration AND definition, we have a layout
219             MemoryLayout layout = TypeMaker.valueLayoutForSize(c.type().size() * 8).layout().orElseThrow();
220             return factoryLayout.make(CursorPosition.of(c), c.spelling(), layout, decls.toArray(new Declaration[0]));
221         } else {
222             //just a declaration
223             //if there's a real definition somewhere else, skip this redundant declaration
224             if (!c.getDefinition().isInvalid()) {
225                 return null;
226             }
227             return factoryNoLayout.make(CursorPosition.of(c), c.spelling(), decls.toArray(new Declaration[0]));
228         }
229     }
230 
231     private static boolean isEnum(Declaration d) {
232         return d instanceof Declaration.Scoped && ((Declaration.Scoped)d).kind() == Declaration.Scoped.Kind.ENUM;
233     }
234 
235     private static boolean isBitfield(Declaration d) {
236         return d instanceof Declaration.Scoped && ((Declaration.Scoped)d).kind() == Declaration.Scoped.Kind.BITFIELDS;
237     }
238 
239     private static boolean isAnonymousStruct(Declaration declaration) {
240         return ((CursorPosition)declaration.pos()).cursor.isAnonymousStruct();
241     }
242 
243     private List<Declaration> filterNestedDeclarations(List<Declaration> declarations) {
244         return declarations.stream()
245                 .filter(Objects::nonNull)
246                 .filter(d -> isEnum(d) || !d.name().isEmpty() || isAnonymousStruct(d) || isBitfield(d))
247                 .collect(Collectors.toList());
248     }
249 
250     private Declaration.Typedef createTypedef(Cursor c) {
251         Type cursorType = toType(c);
252         Type canonicalType = canonicalType(cursorType);
253         if (canonicalType instanceof Type.Declared) {
254             Declaration.Scoped s = ((Type.Declared) canonicalType).tree();
255             if (s.name().equals(c.spelling())) {
256                 // typedef record with the same name, no need to present twice
257                 return null;
258             }
259         }
260         Type.Function funcType = null;
261         boolean isFuncPtrType = false;
262         if (canonicalType instanceof Type.Function) {
263             funcType = (Type.Function)canonicalType;
264         } else if (Utils.isPointerType(canonicalType)) {
265             Type pointeeType = null;
266             try {
267                 pointeeType = ((Type.Delegated)canonicalType).type();
268             } catch (NullPointerException npe) {
269                 // exception thrown for unresolved pointee type. Ignore if we hit that case.
270             }
271             if (pointeeType instanceof Type.Function) {
272                 funcType = (Type.Function)pointeeType;
273                 isFuncPtrType = true;
274             }
275         }
276         if (funcType != null) {
277             List<String> params = c.children().
278                 filter(ch -> ch.kind() == CursorKind.ParmDecl).
279                 map(this::createTree).
280                 map(Declaration.Variable.class::cast).
281                 map(Declaration::name).
282                 collect(Collectors.toList());
283             if (!params.isEmpty()) {
284                 canonicalType = funcType.withParameterNames(params);
285                 if (isFuncPtrType) {
286                     canonicalType = new TypeImpl.PointerImpl(canonicalType);
287                 }
288             }
289         }
290         return Declaration.typedef(CursorPosition.of(c), c.spelling(), canonicalType);
291     }
292 
293     private Type canonicalType(Type t) {
294         if (t instanceof Type.Delegated delegated &&
295            delegated.kind() == Type.Delegated.Kind.TYPEDEF) {
296             return delegated.type();
297         } else {
298             return t;
299         }
300     }
301 
302     private Declaration.Variable createVar(Declaration.Variable.Kind kind, Cursor c, VarFactoryNoLayout varFactory) {
303         checkCursorAny(c, CursorKind.VarDecl, CursorKind.FieldDecl, CursorKind.ParmDecl);
304         if (c.isBitField()) {
305             return Declaration.bitfield(CursorPosition.of(c), c.spelling(), toType(c),
306                     MemoryLayout.paddingLayout(c.getBitFieldWidth()));
307         } else {
308             Type type = null;
309             try {
310                 type = toType(c);
311             } catch (TypeMaker.TypeException ex) {
312                 System.err.println(ex);
313                 System.err.println("WARNING: ignoring variable: " + c.spelling());
314                 return null;
315             }
316             return varFactory.make(CursorPosition.of(c), c.spelling(), type);
317         }
318     }
319 
320     private Type toType(Cursor c) {
321         return typeMaker.makeType(c.type());
322     }
323 
324     private void checkCursor(Cursor c, CursorKind k) {
325         if (c.kind() != k) {
326             throw new IllegalArgumentException("Invalid cursor kind");
327         }
328     }
329 
330     private void checkCursorAny(Cursor c, CursorKind... kinds) {
331         CursorKind expected = Objects.requireNonNull(c.kind());
332         for (CursorKind k : kinds) {
333             if (k == expected) {
334                 return;
335             }
336         }
337         throw new IllegalArgumentException("Invalid cursor kind");
338     }
339 }