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 
 30 import java.nio.ByteOrder;
 31 import java.util.ArrayList;
 32 import java.util.ConcurrentModificationException;
 33 import java.util.HashMap;
 34 import java.util.List;
 35 import java.util.Map;
 36 import java.util.Objects;
 37 import java.util.function.Supplier;
 38 
 39 import jdk.incubator.foreign.MemoryLayout;
 40 import jdk.incubator.jextract.Declaration;
 41 import jdk.incubator.jextract.Type;
 42 import jdk.incubator.jextract.Type.Delegated;
 43 import jdk.incubator.jextract.Type.Primitive;
 44 import jdk.internal.clang.Cursor;
 45 
 46 class TypeMaker {
 47 
 48     TreeMaker treeMaker;
 49     private final Map<jdk.internal.clang.Type, Type> typeCache = new HashMap<>();
 50     private List<ClangTypeReference> unresolved = new ArrayList<>();
 51 
 52     private class ClangTypeReference implements Supplier<Type> {
 53         jdk.internal.clang.Type origin;
 54         Type derived;
 55 
 56         private ClangTypeReference(jdk.internal.clang.Type origin) {
 57             this.origin = origin;
 58             derived = typeCache.get(origin);
 59         }
 60 
 61         public boolean isUnresolved() {
 62             return null == derived;
 63         }
 64 
 65         public void resolve() {
 66             derived = makeType(origin);
 67             Objects.requireNonNull(derived, "Clang type cannot be resolved: " + origin.spelling());
 68         }
 69 
 70         public Type get() {
 71             Objects.requireNonNull(derived, "Type is not yet resolved.");
 72             return derived;
 73         }
 74     }
 75 
 76     private ClangTypeReference reference(jdk.internal.clang.Type type) {
 77         ClangTypeReference ref = new ClangTypeReference(type);
 78         if (ref.isUnresolved()) {
 79             unresolved.add(ref);
 80         }
 81         return ref;
 82     }
 83 
 84     public TypeMaker(TreeMaker treeMaker) {
 85         this.treeMaker = treeMaker;
 86     }
 87 
 88     /**
 89      * Resolve all type references. This method should be called before discard clang cursors/types
 90      */
 91     void resolveTypeReferences() {
 92         List<ClangTypeReference> resolving = unresolved;
 93         unresolved = new ArrayList<>();
 94         while (! resolving.isEmpty()) {
 95             resolving.forEach(ClangTypeReference::resolve);
 96             resolving = unresolved;
 97             unresolved = new ArrayList<>();
 98         }
 99     }
100 
101     Type makeType(jdk.internal.clang.Type t) {
102         Type rv = typeCache.get(t);
103         if (rv != null) {
104             return rv;
105         }
106         rv = makeTypeInternal(t);
107         if (null != rv && typeCache.put(t, rv) != null) {
108             throw new ConcurrentModificationException();
109         }
110         return rv;
111     }
112 
113     static class TypeException extends RuntimeException {
114         static final long serialVersionUID = 1L;
115 
116         TypeException(String msg) {
117             super(msg);
118         }
119     }
120 
121     Type makeTypeInternal(jdk.internal.clang.Type t) {
122         switch(t.kind()) {
123             case Auto:
124                 return makeType(t.canonicalType());
125             case Void:
126                 return Type.void_();
127             case Char_S:
128             case Char_U:
129                 return Type.primitive(Primitive.Kind.Char);
130             case Short:
131                 return Type.primitive(Primitive.Kind.Short);
132             case Int:
133                 return Type.primitive(Primitive.Kind.Int);
134             case Long:
135                 return Type.primitive(Primitive.Kind.Long);
136             case LongLong:
137                 return Type.primitive(Primitive.Kind.LongLong);
138             case SChar: {
139                 Type chType = Type.primitive(Primitive.Kind.Char);
140                 return Type.qualified(Delegated.Kind.SIGNED, chType);
141             }
142             case UShort: {
143                 Type chType = Type.primitive(Primitive.Kind.Short);
144                 return Type.qualified(Delegated.Kind.UNSIGNED, chType);
145             }
146             case UInt: {
147                 Type chType = Type.primitive(Primitive.Kind.Int);
148                 return Type.qualified(Delegated.Kind.UNSIGNED, chType);
149             }
150             case ULong: {
151                 Type chType = Type.primitive(Primitive.Kind.Long);
152                 return Type.qualified(Delegated.Kind.UNSIGNED, chType);
153             }
154             case ULongLong: {
155                 Type chType = Type.primitive(Primitive.Kind.LongLong);
156                 return Type.qualified(Delegated.Kind.UNSIGNED, chType);
157             }
158             case UChar: {
159                 Type chType = Type.primitive(Primitive.Kind.Char);
160                 return Type.qualified(Delegated.Kind.UNSIGNED, chType);
161             }
162 
163             case Bool:
164                 return Type.primitive(Primitive.Kind.Bool);
165             case Double:
166                 return Type.primitive(Primitive.Kind.Double);
167             case Float:
168                 return Type.primitive(Primitive.Kind.Float);
169             case Unexposed:
170             case Elaborated:
171                 jdk.internal.clang.Type canonical = t.canonicalType();
172                 if (canonical.equalType(t)) {
173                     throw new TypeException("Unknown type with same canonical type: " + t.spelling());
174                 }
175                 return makeType(canonical);
176             case ConstantArray: {
177                 Type elem = makeType(t.getElementType());
178                 return Type.array(t.getNumberOfElements(), elem);
179             }
180             case IncompleteArray: {
181                 Type elem = makeType(t.getElementType());
182                 return Type.array(elem);
183             }
184             case FunctionProto:
185             case FunctionNoProto: {
186                 List<Type> args = new ArrayList<>();
187                 for (int i = 0; i < t.numberOfArgs(); i++) {
188                     // argument could be function pointer declared locally
189                     args.add(lowerFunctionType(t.argType(i)));
190                 }
191                 return Type.function(t.isVariadic(), lowerFunctionType(t.resultType()), args.toArray(new Type[0]));
192             }
193             case Enum:
194             case Record: {
195                 return Type.declared((Declaration.Scoped) treeMaker.createTree(t.getDeclarationCursor()));
196             }
197             case BlockPointer:
198             case Pointer: {
199                 // TODO: We can always erase type for macro evaluation, should we?
200                 return new TypeImpl.PointerImpl(reference(t.getPointeeType()));
201             }
202             case Typedef: {
203                 Type __type = makeType(t.canonicalType());
204                 return Type.typedef(t.spelling(), __type);
205             }
206             case Complex: {
207                 Type __type = makeType(t.getElementType());
208                 return Type.qualified(Delegated.Kind.COMPLEX, __type);
209             }
210             case Vector: {
211                 Type __type = makeType(t.getElementType());
212                 return Type.vector(t.getNumberOfElements(), __type);
213             }
214             case WChar: //unsupported
215                 return Type.primitive(Primitive.Kind.WChar);
216             case Char16: //unsupported
217                 return Type.primitive(Primitive.Kind.Char16);
218             case Half: //unsupported
219                 return Type.primitive(Primitive.Kind.HalfFloat);
220             case Int128: //unsupported
221                 return Type.primitive(Primitive.Kind.Int128);
222             case LongDouble: //unsupported
223                 return Type.primitive(Primitive.Kind.LongDouble);
224             case UInt128: { //unsupported
225                 Type iType = Type.primitive(Primitive.Kind.Int128);
226                 return Type.qualified(Delegated.Kind.UNSIGNED, iType);
227             }
228             default:
229                 return TypeImpl.ERROR;
230         }
231     }
232 
233     private Type lowerFunctionType(jdk.internal.clang.Type t) {
234         Type t2 = makeType(t);
235         return t2.accept(lowerFunctionType, null);
236     }
237 
238     private Type.Visitor<Type, Void> lowerFunctionType = new Type.Visitor<>() {
239         @Override
240         public Type visitArray(Type.Array t, Void aVoid) {
241             return Type.pointer(t.elementType());
242         }
243 
244         @Override
245         public Type visitDelegated(Type.Delegated t, Void aVoid) {
246             if (t.kind() == Delegated.Kind.TYPEDEF && t.type() instanceof Type.Array) {
247                 return visitArray((Type.Array)t.type(), aVoid);
248             }
249             return visitType(t, aVoid);
250         }
251 
252         @Override
253         public Type visitType(Type t, Void aVoid) {
254             return t;
255         }
256     };
257 
258     public static Primitive.Kind valueLayoutForSize(long size) {
259         return switch ((int) size) {
260             case 8 -> Primitive.Kind.Char;
261             case 16 -> Primitive.Kind.Short;
262             case 32 -> Primitive.Kind.Int;
263             case 64 -> Primitive.Kind.LongLong;
264             default -> throw new IllegalStateException("Cannot infer container layout");
265         };
266     }
267 }