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