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 import java.nio.file.Path;
 25 import java.nio.file.Paths;
 26 import java.util.ArrayList;
 27 import java.util.List;
 28 import java.util.Optional;
 29 import java.util.function.Predicate;
 30 
 31 import jdk.incubator.jextract.Declaration;
 32 import jdk.incubator.jextract.JextractTool;
 33 import jdk.incubator.jextract.Type;
 34 
 35 import static org.testng.Assert.assertEquals;
 36 import static org.testng.Assert.assertTrue;
 37 import static org.testng.Assert.fail;
 38 
 39 public class JextractApiTestBase {
 40     static final boolean isMacOSX =
 41             System.getProperty("os.name", "unknown").contains("OS X");
 42     static final boolean isWindows =
 43             System.getProperty("os.name", "unknown").startsWith("Windows");
 44 
 45     public static  Declaration.Scoped parse(String headerFilename, String... parseOptions) {
 46         Path header = Paths.get(System.getProperty("test.src", "."), headerFilename);
 47         return JextractTool.parse(List.of(header), parseOptions);
 48     }
 49 
 50     public static void checkNames(List<Declaration> members, String... fields) {
 51         assertEquals(members.size(), fields.length);
 52         for (int i = 0; i < fields.length; i++) {
 53             assertEquals(members.get(i).name(), fields[i]);
 54         }
 55     }
 56 
 57     public static Declaration.Scoped checkScoped(Declaration.Scoped toplevel, String name, Declaration.Scoped.Kind kind,  String... fields) {
 58         Declaration.Scoped scoped = findDecl(toplevel, name, Declaration.Scoped.class);
 59         assertTrue(scoped.kind() == kind);
 60         checkNames(scoped.members(), fields);
 61         return scoped;
 62     }
 63 
 64     private static List<Declaration> getNamedFields(Declaration.Scoped scoped) {
 65         List<Declaration> fields = new ArrayList<>();
 66         scoped.members().forEach(d -> {
 67             if (d instanceof Declaration.Variable) {
 68                 Declaration.Variable v = (Declaration.Variable) d;
 69                 if (v.kind() == Declaration.Variable.Kind.FIELD) {
 70                     assert (!v.name().isEmpty());
 71                     fields.add(v);
 72                 }
 73             } else if (d instanceof Declaration.Scoped) {
 74                 Declaration.Scoped record = (Declaration.Scoped) d;
 75                 if (record.name().isEmpty()) {
 76                     fields.addAll(getNamedFields(record));
 77                 } else {
 78                     fields.add(record);
 79                 }
 80             }
 81         });
 82         return fields;
 83     }
 84 
 85     public static Declaration.Scoped checkRecord(Declaration.Scoped scoped, String name, Declaration.Scoped.Kind kind,  String... fields) {
 86         assertTrue(scoped.kind() == kind);
 87         checkNames(getNamedFields(scoped), fields);
 88         return scoped;
 89     }
 90 
 91     public static Declaration.Scoped checkStruct(Declaration.Scoped toplevel, String name, String... fields) {
 92         return checkScoped(toplevel, name, Declaration.Scoped.Kind.STRUCT, fields);
 93     }
 94 
 95     public static Declaration.Scoped checkBitfields(Declaration.Scoped toplevel, String name, String... fields) {
 96         return checkScoped(toplevel, name, Declaration.Scoped.Kind.BITFIELDS, fields);
 97     }
 98 
 99     public static Declaration.Scoped checkUnion(Declaration.Scoped toplevel, String name, String... fields) {
100         return checkScoped(toplevel, name, Declaration.Scoped.Kind.UNION, fields);
101     }
102 
103     public static Declaration.Variable checkVariable(Declaration.Scoped scope, String name, Type type) {
104         Declaration.Variable var = findDecl(scope, name, Declaration.Variable.class);
105         assertTypeEquals(type, var.type());
106         return var;
107     }
108 
109     public static Declaration.Variable checkGlobal(Declaration.Scoped toplevel, String name, Type type) {
110         Declaration.Variable global = checkVariable(toplevel, name, type);
111         assertEquals(global.kind(), Declaration.Variable.Kind.GLOBAL);
112         return global;
113     }
114 
115     public static Declaration.Variable checkField(Declaration.Scoped record, String name, Type type) {
116         Declaration.Variable global = checkVariable(record, name, type);
117         assertEquals(global.kind(), Declaration.Variable.Kind.FIELD);
118         return global;
119     }
120 
121     public static Declaration.Variable checkBitField(Declaration.Scoped record, String name, Type type, int size) {
122         Declaration.Variable global = checkVariable(record, name, type);
123         assertEquals(global.kind(), Declaration.Variable.Kind.BITFIELD);
124         assertEquals(global.layout().get().bitSize(), size);
125         return global;
126     }
127 
128     public static void checkFunction(Declaration.Function function, Type ret, Type... params) {
129         assertTypeEquals(ret, function.type().returnType());
130         assertEquals(function.parameters().size(), params.length);
131         for (int i = 0 ; i < params.length ; i++) {
132             assertTypeEquals(params[i], function.type().argumentTypes().get(i));
133             Type paramType = function.parameters().get(i).type();
134             if (paramType instanceof Type.Array) {
135                 assertTypeEquals(params[i], Type.pointer(((Type.Array) paramType).elementType()));
136             } else {
137                 assertTypeEquals(params[i], function.parameters().get(i).type());
138             }
139         }
140     }
141 
142     public static Declaration.Function checkFunction(Declaration.Scoped toplevel,String name , Type ret, Type... params) {
143         Declaration.Function function = findDecl(toplevel, name, Declaration.Function.class);
144         checkFunction(function, ret,params);
145         return function;
146     }
147 
148     public static Declaration.Constant checkConstant(Declaration.Scoped toplevel, String name, Type type, Object value) {
149         Declaration.Constant constant = findDecl(toplevel, name, Declaration.Constant.class);
150         assertTypeEquals(type, constant.type());
151         assertEquals(value, constant.value());
152         return constant;
153     }
154 
155     public static Predicate<Declaration> byName(final String name) {
156         return d -> d.name().equals(name);
157     }
158 
159     public static Predicate<Declaration> byNameAndType(final String name, Class<? extends Declaration> declType) {
160         return d -> declType.isAssignableFrom(d.getClass()) && d.name().equals(name);
161     }
162 
163     public static Optional<Declaration> findDecl(Declaration.Scoped toplevel, Predicate<Declaration> filter) {
164         return toplevel.members().stream().filter(filter).findAny();
165     }
166 
167     @SuppressWarnings("unchecked")
168     public static <D extends Declaration> D findDecl(Declaration.Scoped toplevel, String name, Class<D> declType) {
169         Optional<Declaration> d = findDecl(toplevel, byNameAndType(name, declType));
170         if (d.isEmpty()) {
171             fail("No declaration with name " + name + " found in " + toplevel);
172             return null;
173         }
174         return (D) d.get();
175     }
176 
177     public static void assertTypeEquals(Type expected, Type found) {
178         assertEquals(expected.getClass(), found.getClass());
179         if (expected instanceof Type.Primitive) {
180             assertEquals(expected, found);
181         } else if (expected instanceof Type.Delegated) {
182             assertEquals(((Type.Delegated)expected).kind(), ((Type.Delegated)found).kind());
183             assertTypeEquals(((Type.Delegated)expected).type(), ((Type.Delegated)found).type());
184         } else if (expected instanceof Type.Array) {
185             assertEquals(((Type.Array)expected).kind(), ((Type.Array)found).kind());
186             assertEquals(((Type.Array)expected).elementCount(), ((Type.Array)found).elementCount());
187             assertTypeEquals(((Type.Array)expected).elementType(), ((Type.Array)found).elementType());
188         } else if (expected instanceof Type.Declared) {
189             assertEquals(((Type.Declared)expected).tree(), ((Type.Declared)found).tree());
190         } else if (expected instanceof Type.Function) {
191             assertTypeEquals(((Type.Function)expected).returnType(), ((Type.Function)found).returnType());
192             assertEquals(((Type.Function)expected).argumentTypes().size(), ((Type.Function)found).argumentTypes().size());
193             assertEquals(((Type.Function)expected).varargs(), ((Type.Function)found).varargs());
194             for (int i = 0 ; i < ((Type.Function)expected).argumentTypes().size() ; i++) {
195                 assertTypeEquals(((Type.Function)expected).argumentTypes().get(i), ((Type.Function)found).argumentTypes().get(i));
196             }
197         }
198     }
199 
200     public static Type unwrapDelegatedType(Type type, Type.Delegated.Kind kind) {
201         assertTrue(type instanceof Type.Delegated,
202                 "Expecting Type.Delegated, got " + type.getClass());
203         Type.Delegated delegated = (Type.Delegated) type;
204         assertEquals(delegated.kind(), kind);
205         return delegated.type();
206     }
207 
208     public static Type unwrapPointerType(Type type) {
209         return unwrapDelegatedType(type, Type.Delegated.Kind.POINTER);
210     }
211 
212     public static Type unwrapTypedefType(Type type) {
213         return unwrapDelegatedType(type, Type.Delegated.Kind.TYPEDEF);
214     }
215 
216     public static Type unwrapArrayType(Type type, long size) {
217         assertTrue(type instanceof Type.Array,
218                 "Expecting Type.Array, got " + type.getClass());
219         Type.Array arType = (Type.Array) type;
220         assertEquals(arType.elementCount().getAsLong(), size);
221         return arType.elementType();
222     }
223 
224     public static Type unwrapArrayType(Type type) {
225         assertTrue(type instanceof Type.Array,
226                 "Expecting Type.Array, got " + type.getClass());
227         Type.Array arType = (Type.Array) type;
228         assertTrue(arType.elementCount().isEmpty());
229         return arType.elementType();
230     }
231 
232     static class TypeUnwrapper {
233         private Type type;
234 
235         private TypeUnwrapper(Type type) {
236             this.type = type;
237         }
238 
239         public static TypeUnwrapper of(Type type) {
240             return new TypeUnwrapper(type);
241         }
242 
243         public TypeUnwrapper unwrapPointer() {
244             type = unwrapPointerType(type);
245             return this;
246         }
247 
248         public TypeUnwrapper unwrapTypedef() {
249             type = unwrapTypedefType(type);
250             return this;
251         }
252 
253         public TypeUnwrapper unwrapArray(long size) {
254             type = unwrapArrayType(type, size);
255             return this;
256         }
257 
258         public TypeUnwrapper unwrapArray() {
259             type = unwrapArrayType(type);
260             return this;
261         }
262 
263         public Type get() {
264             return type;
265         }
266     }
267 }