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