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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @build JextractApiTestBase
 27  * @run testng/othervm -ea --enable-native-access=jdk.incubator.jextract TestTypedef
 28  */
 29 
 30 import java.util.Set;
 31 import java.util.Optional;
 32 import java.util.function.Consumer;
 33 import jdk.incubator.jextract.Declaration;
 34 import jdk.incubator.jextract.Type;
 35 
 36 import org.testng.annotations.BeforeClass;
 37 import org.testng.annotations.Test;
 38 
 39 import static org.testng.Assert.assertEquals;
 40 import static org.testng.Assert.assertNotEquals;
 41 import static org.testng.Assert.assertTrue;
 42 import static org.testng.Assert.fail;
 43 
 44 public class TestTypedef extends JextractApiTestBase {
 45     Declaration.Scoped root;
 46 
 47     @BeforeClass
 48     public void parse() {
 49         root = parse("testTypedef.h");
 50         System.out.println(root);
 51     }
 52 
 53     private Declaration[] findAllWithName(Declaration.Scoped scope, String name) {
 54         return scope.members().stream().filter(byName(name)).toArray(Declaration[]::new);
 55     }
 56 
 57     public static Type getTypedefType(Declaration.Scoped scope, String name) {
 58         Declaration.Typedef d = findDecl(scope, name, Declaration.Typedef.class);
 59         Type type = d.type();
 60         // Typedef declaration should return canonical type
 61         if (type instanceof Type.Delegated) {
 62             assertNotEquals(((Type.Delegated) type).kind(), Type.Delegated.Kind.TYPEDEF);
 63         }
 64         return d.type();
 65     }
 66 
 67     private Declaration.Scoped assertDeclaredTypedef(Declaration.Typedef decl) {
 68         Type type = decl.type();
 69         assertTrue(type instanceof Type.Declared, "Expecting Type.Declared, got " + type.getClass());
 70         return ((Type.Declared) type).tree();
 71     }
 72 
 73     private Declaration.Scoped assertAnonymousRecord(Declaration.Scoped scope, String name) {
 74         Declaration[] ar = findAllWithName(scope, name);
 75         assertEquals(ar.length, 1);
 76         assertTrue(ar[0] instanceof Declaration.Typedef, "Expectint Declaration.Typedef, but got " + ar[0].getClass());
 77         Declaration.Scoped record = assertDeclaredTypedef((Declaration.Typedef) ar[0]);
 78         return record;
 79     }
 80 
 81     private Declaration.Scoped assertNamedRecord(Declaration.Scoped scope, String name) {
 82         Declaration[] ar = findAllWithName(scope, name);
 83         assertEquals(ar.length, 1);
 84         assertTrue(ar[0] instanceof Declaration.Scoped, "Expectint Declaration.Scoped, but got " + ar[0].getClass());
 85         return (Declaration.Scoped) ar[0];
 86     }
 87 
 88     @Test
 89     public void NoDuplicateSameNameTypedef() {
 90         // When typedef a named record with the same name, present the scoped
 91         // declaration and ignore the typedef
 92         Declaration.Scoped s = assertNamedRecord(root, "Point3D");
 93         assertEquals(s.kind(), Declaration.Scoped.Kind.STRUCT);
 94         checkNames(s.members(), "i", "j", "k");
 95 
 96         s = assertNamedRecord(root, "SIZE");
 97         assertEquals(s.kind(), Declaration.Scoped.Kind.ENUM);
 98         checkNames(s.members(), "XS", "S", "M", "L", "XL");
 99     }
100 
101     @Test
102     public void TypedefReferences() {
103         // When reference to a typedef, the Type should be Type.Delegated
104         // With the type to be the referenced type
105         Declaration.Scoped pt3d = checkStruct(root, "Point3D", "i", "j", "k");
106         Declaration.Function drawParamid = findDecl(root, "drawParamid", Declaration.Function.class);
107         Type.Function fnType = drawParamid.type();
108         // Array in function argument is lowered to pointer
109         Type type = TypeUnwrapper.of(fnType.argumentTypes().get(0))
110                         .unwrapPointer().unwrapTypedef().get();
111         assertEquals(type, Type.declared(pt3d));
112 
113         Declaration.Function do_ops = findDecl(root, "do_ops", Declaration.Function.class);
114         fnType = do_ops.type();
115         type = unwrapTypedefType(fnType.returnType());
116         assertEquals(type, getTypedefType(root, "op_sequence"));
117         type = fnType.argumentTypes().get(0);
118         type = unwrapTypedefType(type);
119         assertEquals(type, getTypedefType(root, "int_op"));
120         type = fnType.argumentTypes().get(1);
121         type = unwrapTypedefType(type);
122         assertEquals(type, getTypedefType(root, "count_t"));
123     }
124 
125     @Test
126     public void TypedefsToSameType()  {
127         // For typedef declaration, the type will be the canonical type
128         // Which means, the type will not be another typedef
129         // However, it can be other delegated type or an array
130         Declaration.Scoped pt = checkStruct(root, "Point", "i", "j");
131         Type.Declared type = Type.declared(pt);
132         assertEquals(getTypedefType(root, "POINT"), type);
133         assertEquals(getTypedefType(root, "point_t"), type);
134 
135         Type canonical = TypeUnwrapper.of(getTypedefType(root, "rectangle"))
136                 .unwrapArray(4)
137                 // FIXME? If we would like to generate array using typedef type
138                 // then we need to use typedef as array element type and
139                 // requires following line to pass the test.
140                 // .unwrapTypedef()
141                 .get();
142         assertEquals(canonical, type);
143 
144         Declaration.Variable canvas = findDecl(root, "canvas", Declaration.Variable.class);
145         assertEquals(canvas.kind(), Declaration.Variable.Kind.GLOBAL);
146         Type ref = TypeUnwrapper.of(canvas.type())
147             .unwrapTypedef()
148             .unwrapArray(4)
149             .get();
150         assertEquals(ref, type);
151 
152         getTypedefType(root, "count_t");
153     }
154 
155     @Test
156     public void TypedefsArrays()  {
157         Type intType = getTypedefType(root, "cordinate_t");
158 
159         // As noted earlier, we currently have canonical array element type from typedef
160         Type type = getTypedefType(root, "location2D");
161         Type elementType = unwrapArrayType(type, 2);
162         assertEquals(elementType, intType);
163 
164         Type count_t = getTypedefType(root, "count_t");
165         type = getTypedefType(root, "dimensions");
166         elementType = unwrapArrayType(type);
167         assertEquals(elementType, count_t);
168         type = getTypedefType(root, "count_ptr");
169         assertEquals(type, Type.pointer(count_t));
170     }
171 
172     @Test
173     public void AnonymousRecordTypedef() {
174         // For anonymous typedef, present the typedef declaration and
175         // the Scope declaration can be obtained via Variable.type()
176         Declaration.Scoped record = assertAnonymousRecord(root, "op_sequence");
177         assertEquals(record.kind(), Declaration.Scoped.Kind.STRUCT);
178         checkNames(record.members(), "times", "op");
179 
180         record = assertAnonymousRecord(root, "IntOrFloat");
181         assertEquals(record.kind(), Declaration.Scoped.Kind.UNION);
182         checkNames(record.members(), "i", "f");
183 
184         record = assertAnonymousRecord(root, "codetype_t");
185         assertEquals(record.kind(), Declaration.Scoped.Kind.ENUM);
186         checkNames(record.members(), "Java", "C", "CPP", "Python", "Ruby");
187     }
188 
189     @Test
190     public void CheckAnonyousDeclarations() {
191         // Should we expunge anonymous declaration?
192         // They only needed if referenced as a field or gloabal variable
193         // Exception enum, as they can be used as pleased, so we need to
194         // elevate them into constants.
195         // Anyhow, current implementation pass through enum, not elevate them.
196         // So we just check that
197         Declaration[] ar = findAllWithName(root, "");
198         assertEquals(ar.length, 2);
199         Declaration.Scoped e = (Declaration.Scoped) ar[0];
200         assertEquals(e.kind(), Declaration.Scoped.Kind.ENUM);
201         checkNames(e.members(), "RED", "GREEN", "BLUE");
202         e = (Declaration.Scoped) ar[1];
203         assertEquals(e.kind(), Declaration.Scoped.Kind.ENUM);
204         checkNames(e.members(), "Java", "C", "CPP", "Python", "Ruby");
205     }
206 
207     @Test
208     public void CheckFunctionPointers() {
209         Type intType = getTypedefType(root, "cordinate_t");
210         Type intOpType = getTypedefType(root, "int_op");
211         assertEquals(intOpType, Type.pointer(Type.function(false, intType, intType)));
212         Type intOp2Type = getTypedefType(root, "int_op2");
213         assertEquals(intOp2Type, Type.pointer(Type.function(false, intType, intType, intType)));
214 
215         checkGlobal(root, "another_int_op", intOpType);
216 
217         Declaration.Function getFn = findDecl(root, "getFn", Declaration.Function.class);
218         assertEquals(getFn.parameters().size(), 0);
219         Type.Delegated retType = (Type.Delegated) getFn.type().returnType();
220         assertTrue(retType.kind() == Type.Delegated.Kind.POINTER);
221         Type.Function fnType = (Type.Function) retType.type();
222         assertEquals(fnType.returnType(), Type.void_());
223         assertEquals(fnType.argumentTypes().get(1),
224                 Type.typedef("count_t", getTypedefType(root, "count_t")));
225     }
226 }