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 import jdk.internal.clang.TypeKind;
 35 
 36 import java.util.List;
 37 
 38 /**
 39  * MemoryLayout computer for C unions.
 40  */
 41 final class UnionLayoutComputer extends RecordLayoutComputer {
 42     private final long offset;
 43     private long actualSize = 0L;
 44 
 45     UnionLayoutComputer(TypeMaker typeMaker, long offsetInParent, Type parent, Type type) {
 46         super(typeMaker, parent, type);
 47         this.offset = offsetInParent;
 48     }
 49 
 50     @Override
 51     void processField(Cursor c) {
 52         long expectedOffset = offsetOf(parent, c);
 53         if (expectedOffset > offset) {
 54             throw new IllegalStateException("No padding in union elements!");
 55         }
 56 
 57         addField(offset, parent, c);
 58         actualSize = Math.max(actualSize, fieldSize(c));
 59     }
 60 
 61     @Override
 62     void startBitfield() {
 63         // do nothing
 64     }
 65 
 66     @Override
 67     Declaration field(Cursor c) {
 68         if (c.isBitField()) {
 69             Declaration.Variable var = (Declaration.Variable)super.field(c);
 70             return bitfield(List.of(var.layout().get()), var);
 71         } else {
 72             return super.field(c);
 73         }
 74     }
 75 
 76     @Override
 77     long fieldSize(Cursor c) {
 78         if (c.type().kind() == TypeKind.IncompleteArray) {
 79             return 0;
 80         } else if (c.isBitField()) {
 81             return c.getBitFieldWidth();
 82         } else {
 83             return c.type().size() * 8;
 84         }
 85     }
 86 
 87     @Override
 88     jdk.incubator.jextract.Type.Declared finishRecord(String anonName) {
 89         // size mismatch indicates use of bitfields in union
 90         long expectedSize = type.size() * 8;
 91         if (actualSize < expectedSize) {
 92             // emit an extra padding of expected size to make sure union layout size is computed correctly
 93             addPadding(expectedSize);
 94         } else if (actualSize > expectedSize) {
 95             throw new AssertionError("Invalid union size - expected: " + expectedSize + "; found: " + actualSize);
 96         }
 97 
 98         MemoryLayout[] fields = fieldLayouts.toArray(new MemoryLayout[0]);
 99         GroupLayout g = MemoryLayout.unionLayout(fields);
100         if (!cursor.spelling().isEmpty()) {
101             g = g.withName(cursor.spelling());
102         } else if (anonName != null) {
103             g = g.withName(anonName);
104         }
105         return jdk.incubator.jextract.Type.declared(Declaration.union(new TreeMaker.CursorPosition(cursor), cursor.spelling(), g, fieldDecls.stream().toArray(Declaration[]::new)));
106     }
107 }