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 
 27 package jdk.internal.jextract.impl;
 28 
 29 import jdk.incubator.foreign.MemoryLayout;
 30 import jdk.incubator.jextract.Declaration;
 31 import jdk.internal.clang.Cursor;
 32 import jdk.internal.clang.CursorKind;
 33 import jdk.internal.clang.Type;
 34 import jdk.internal.clang.TypeKind;
 35 
 36 import java.util.ArrayList;
 37 import java.util.List;
 38 import java.util.stream.Collectors;
 39 import java.util.stream.Stream;
 40 
 41 /**
 42  * Base class for C struct, union MemoryLayout computer helper classes.
 43  */
 44 abstract class RecordLayoutComputer {
 45     // enclosing struct type (or this struct type for top level structs)
 46     final Type parent;
 47     // this struct type
 48     final Type type;
 49     // cursor of this struct
 50     final Cursor cursor;
 51     final List<Declaration> fieldDecls;
 52     final List<MemoryLayout> fieldLayouts;
 53 
 54     final TypeMaker typeMaker;
 55 
 56     private int anonCount = 0;
 57 
 58     RecordLayoutComputer(TypeMaker typeMaker, Type parent, Type type) {
 59         this.parent = parent;
 60         this.type = type;
 61         this.cursor = type.getDeclarationCursor().getDefinition();
 62         this.fieldDecls = new ArrayList<>();
 63         this.fieldLayouts = new ArrayList<>();
 64         this.typeMaker = typeMaker;
 65     }
 66 
 67     static jdk.incubator.jextract.Type compute(TypeMaker typeMaker, long offsetInParent, Type parent, Type type) {
 68         return computeInternal(typeMaker, offsetInParent, parent, type, null);
 69     }
 70 
 71     private static jdk.incubator.jextract.Type computeAnonymous(TypeMaker typeMaker, long offsetInParent, Type parent, Type type, String name) {
 72         return computeInternal(typeMaker, offsetInParent, parent, type, name);
 73     }
 74 
 75     static final jdk.incubator.jextract.Type.Declared ERRONEOUS = jdk.incubator.jextract.Type.declared(
 76             Declaration.struct(TreeMaker.CursorPosition.NO_POSITION, "", MemoryLayout.paddingLayout(64)));
 77 
 78     private static jdk.incubator.jextract.Type computeInternal(TypeMaker typeMaker, long offsetInParent, Type parent, Type type, String name) {
 79         Cursor cursor = type.getDeclarationCursor().getDefinition();
 80         if (cursor.isInvalid()) {
 81             return ERRONEOUS;
 82         }
 83 
 84         final boolean isUnion = cursor.kind() == CursorKind.UnionDecl;
 85         return isUnion? new UnionLayoutComputer(typeMaker, offsetInParent, parent, type).compute(name) :
 86                 new StructLayoutComputer(typeMaker, offsetInParent, parent, type).compute(name);
 87     }
 88 
 89     final jdk.incubator.jextract.Type.Declared compute(String anonName) {
 90         Stream<Cursor> fieldCursors = Utils.flattenableChildren(cursor);
 91         for (Cursor fc : fieldCursors.collect(Collectors.toList())) {
 92             /*
 93              * Ignore bitfields of zero width.
 94              *
 95              * struct Foo {
 96              *     int i:0;
 97              * }
 98              *
 99              * And bitfields without a name.
100              * (padding is computed automatically)
101              */
102             if (fc.isBitField() && (fc.getBitFieldWidth() == 0 || fc.spelling().isEmpty())) {
103                 startBitfield();
104                 continue;
105             }
106 
107             processField(fc);
108         }
109 
110         return finishRecord(anonName);
111     }
112 
113     abstract void startBitfield();
114     abstract void processField(Cursor c);
115     abstract jdk.incubator.jextract.Type.Declared finishRecord(String anonName);
116 
117     void addField(Declaration declaration) {
118         fieldDecls.add(declaration);
119         MemoryLayout layout = null;
120         if (declaration instanceof Declaration.Scoped scoped) {
121             layout = scoped.layout().orElse(null);
122         } else if (declaration instanceof Declaration.Variable var) {
123             layout = var.layout().orElse(null);
124         }
125         if (layout != null) {
126             //fieldLayouts.add(layout.name().isEmpty() ? layout.withName(declaration.name()) : layout);
127             fieldLayouts.add(declaration.name().isEmpty() ? layout : layout.withName(declaration.name()));
128         }
129     }
130 
131     void addPadding(long bits) {
132         fieldLayouts.add(MemoryLayout.paddingLayout(bits));
133     }
134 
135     void addField(long offset, Type parent, Cursor c) {
136         if (c.isAnonymousStruct()) {
137             addField(((jdk.incubator.jextract.Type.Declared)computeAnonymous(typeMaker, offset, parent, c.type(), nextAnonymousName())).tree());
138         } else {
139             addField(field(c));
140         }
141     }
142 
143     private String nextAnonymousName() {
144         return "$anon$" + anonCount++;
145     }
146 
147     Declaration field(Cursor c) {
148         jdk.incubator.jextract.Type type = typeMaker.makeType(c.type());
149         String name = c.spelling();
150         if (c.isBitField()) {
151             MemoryLayout sublayout = MemoryLayout.paddingLayout(c.getBitFieldWidth());
152             return Declaration.bitfield(TreeMaker.CursorPosition.of(c), name, type, sublayout.withName(name));
153         } else if (c.isAnonymousStruct() && type instanceof jdk.incubator.jextract.Type.Declared decl) {
154             return decl.tree();
155         } else {
156             return Declaration.field(TreeMaker.CursorPosition.of(c), name, type);
157         }
158     }
159 
160     long fieldSize(Cursor c) {
161         if (c.type().kind() == TypeKind.IncompleteArray) {
162             return 0;
163         }
164         return c.isBitField() ? c.getBitFieldWidth() : c.type().size() * 8;
165     }
166 
167     Declaration.Scoped bitfield(List<MemoryLayout> sublayouts, Declaration.Variable... declarations) {
168         return Declaration.bitfields(declarations[0].pos(), MemoryLayout.structLayout(sublayouts.toArray(new MemoryLayout[0])), declarations);
169     }
170 
171     long offsetOf(Type parent, Cursor c) {
172         if (c.kind() == CursorKind.FieldDecl) {
173             return parent.getOffsetOf(c.spelling());
174         } else {
175             return Utils.flattenableChildren(c)
176                     .mapToLong(child -> offsetOf(parent, child))
177                     .findFirst()
178                     .orElseThrow(() -> new IllegalStateException(
179                             "Can not find offset of: " + c + ", in: " + parent));
180         }
181     }
182 }