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