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.GroupLayout;
 30 import jdk.incubator.foreign.MemoryLayout;
 31 import jdk.incubator.jextract.Declaration;
 32 import jdk.internal.clang.Cursor;
 33 import jdk.internal.clang.Type;
 34 
 35 import java.util.ArrayList;
 36 import java.util.List;
 37 
 38 /**
 39  * MemoryLayout computer for C structs.
 40  */
 41 final class StructLayoutComputer extends RecordLayoutComputer {
 42     private long offset;
 43     private long actualSize = 0L;
 44     // List to collect bitfield fields to process later, may be null
 45     private List<Declaration> bitfieldDecls;
 46     private List<MemoryLayout> bitfieldLayouts;
 47 
 48     StructLayoutComputer(TypeMaker typeMaker, long offsetInParent, Type parent, Type type) {
 49         super(typeMaker, parent, type);
 50         this.offset = offsetInParent;
 51     }
 52 
 53     @Override
 54     void addField(Declaration declaration) {
 55         if (bitfieldDecls != null) {
 56             bitfieldDecls.add(declaration);
 57             MemoryLayout layout = null;
 58             if (declaration instanceof Declaration.Scoped scoped) {
 59                 layout = scoped.layout().orElse(null);
 60             } else if (declaration instanceof Declaration.Variable var) {
 61                 layout = var.layout().orElse(null);
 62             }
 63             if (layout != null) {
 64                 bitfieldLayouts.add(declaration.name().isEmpty() ? layout : layout.withName(declaration.name()));
 65             }
 66         } else {
 67             super.addField(declaration);
 68         }
 69     }
 70 
 71     @Override
 72     void addPadding(long bits) {
 73         if (bitfieldDecls != null) {
 74             bitfieldLayouts.add(MemoryLayout.paddingLayout(bits));
 75         } else {
 76             super.addPadding(bits);
 77         }
 78     }
 79 
 80     @Override
 81     void startBitfield() {
 82         /*
 83          * In a struct, a bitfield field is seen after a non-bitfield.
 84          * Initialize bitfieldLayouts list to collect this and subsequent
 85          * bitfield layouts.
 86          */
 87         if (bitfieldDecls == null) {
 88             bitfieldDecls = new ArrayList<>();
 89             bitfieldLayouts = new ArrayList<>();
 90         }
 91     }
 92 
 93     @Override
 94     void processField(Cursor c) {
 95         boolean isBitfield = c.isBitField();
 96         long expectedOffset = offsetOf(parent, c);
 97         if (expectedOffset > offset) {
 98             addPadding(expectedOffset - offset);
 99             actualSize += (expectedOffset - offset);
100             offset = expectedOffset;
101         }
102 
103         if (isBitfield) {
104             startBitfield();
105         } else { // !isBitfield
106             /*
107              * We may be crossing from bit fields to non-bitfield field.
108              *
109              * struct Foo {
110              *     int i:12;
111              *     int j:20;
112              *     int k; // <-- processing this
113              *     int m;
114              * }
115              */
116             handleBitfields();
117         }
118 
119         addField(offset, parent, c);
120         long size = fieldSize(c);
121         offset += size;
122         actualSize += size;
123     }
124 
125     @Override
126     jdk.incubator.jextract.Type.Declared finishRecord(String anonName) {
127         // pad at the end, if any
128         long expectedSize = type.size() * 8;
129         if (actualSize < expectedSize) {
130             addPadding(expectedSize - actualSize);
131         }
132 
133         /*
134          * Handle bitfields at the end, if any.
135          *
136          * struct Foo {
137          *     int i,j, k;
138          *     int f:10;
139          *     int pad:12;
140          * }
141          */
142         handleBitfields();
143 
144         MemoryLayout[] fields = fieldLayouts.toArray(new MemoryLayout[0]);
145         GroupLayout g = MemoryLayout.structLayout(fields);
146         if (!cursor.spelling().isEmpty()) {
147             g = g.withName(cursor.spelling());
148         } else if (anonName != null) {
149             g = g.withName(anonName);
150         }
151         return jdk.incubator.jextract.Type.declared(Declaration.struct(new TreeMaker.CursorPosition(cursor), cursor.spelling(), g, fieldDecls.stream().toArray(Declaration[]::new)));
152     }
153 
154     // process bitfields if any and clear bitfield layouts
155     private void handleBitfields() {
156         if (bitfieldDecls != null) {
157             List<MemoryLayout> prevBitfieldLayouts = bitfieldLayouts;
158             List<Declaration> prevBitfieldDecls = bitfieldDecls;
159             bitfieldDecls = null;
160             if (!prevBitfieldDecls.isEmpty()) {
161                 addField(bitfield(prevBitfieldLayouts, prevBitfieldDecls.toArray(new Declaration.Variable[0])));
162             }
163         }
164     }
165 }