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 package jdk.incubator.jextract.tool; 26 27 import jdk.incubator.jextract.Declaration; 28 import jdk.incubator.jextract.Type; 29 import jdk.incubator.foreign.FunctionDescriptor; 30 import jdk.incubator.foreign.MemoryAddress; 31 import jdk.incubator.foreign.MemoryLayout; 32 import jdk.incubator.foreign.MemorySegment; 33 34 import javax.tools.JavaFileObject; 35 import javax.tools.SimpleJavaFileObject; 36 import java.io.File; 37 import java.io.IOException; 38 import java.io.UncheckedIOException; 39 import java.lang.invoke.MethodType; 40 import java.net.URI; 41 import java.net.URL; 42 import java.net.URISyntaxException; 43 import java.nio.file.Files; 44 import java.nio.file.Paths; 45 import java.util.HashSet; 46 import java.util.List; 47 import java.util.Optional; 48 import java.util.Set; 49 import java.util.stream.Collectors; 50 51 /* 52 * Scan a header file and generate Java source items for entities defined in that header 53 * file. Tree visitor visit methods return true/false depending on whether a 54 * particular Tree is processed or skipped. 55 */ 56 public class HandleSourceFactory implements Declaration.Visitor<Void, Declaration> { 57 58 private final Set<String> constants = new HashSet<>(); 59 protected final JavaSourceBuilder builder = new JavaSourceBuilder(); 60 protected final TypeTranslator typeTranslator = new TypeTranslator(); 61 private final List<String> libraryNames; 62 private final List<String> libraryPaths; 63 private final String clsName; 64 private final String pkgName; 65 66 static JavaFileObject[] generateRaw(Declaration.Scoped decl, String clsName, String pkgName, List<String> libraryNames, List<String> libraryPaths) { 67 return new HandleSourceFactory(clsName, pkgName, libraryNames, libraryPaths).generate(decl); 68 } 69 70 static JavaFileObject[] generateWrapped(Declaration.Scoped decl, String clsName, String pkgName, List<String> libraryNames, List<String> libraryPaths) { 71 return new StaticWrapperSourceFactory(clsName, pkgName, libraryNames, libraryPaths).generate(decl); 72 } 73 74 public HandleSourceFactory(String clsName, String pkgName, List<String> libraryNames, List<String> libraryPaths) { 75 this.libraryNames = libraryNames; 76 this.libraryPaths = libraryPaths; 77 this.clsName = clsName; 78 this.pkgName = pkgName; 79 } 80 81 public JavaFileObject[] generate(Declaration.Scoped decl) { 82 builder.addPackagePrefix(pkgName); 83 builder.classBegin(clsName); 84 builder.addLibraries(libraryNames.toArray(new String[0]), 85 libraryPaths != null ? libraryPaths.toArray(new String[0]) : null); 86 //generate all decls 87 decl.members().forEach(this::generateDecl); 88 89 //generate functional interfaces 90 generateFunctionalInterfaces(decl); 91 92 builder.classEnd(); 93 String src = builder.build(); 94 95 URL runtimeHelper = HandleSourceFactory.class.getResource("resources/RuntimeHelper.template"); 96 97 try { 98 return new JavaFileObject[] { 99 fileFromString(pkgName, clsName, src), 100 fileFromString(pkgName,"RuntimeHelper", (pkgName.isEmpty()? "" : "package " + pkgName + ";\n") + 101 Files.readAllLines(Paths.get(runtimeHelper.toURI())) 102 .stream().collect(Collectors.joining("\n"))) 103 }; 104 } catch (IOException ex) { 105 throw new UncheckedIOException(ex); 106 } catch (URISyntaxException ex2) { 107 throw new RuntimeException(ex2); 108 } 109 } 110 111 protected void generateFunctionalInterfaces(Declaration.Scoped decl) { 112 //generate functional interfaces 113 Set<FunctionDescriptor> functionalInterfaces = new HashSet<>(); 114 new FunctionalInterfaceScanner(functionalInterfaces).scan(decl); 115 functionalInterfaces.forEach(builder::addUpcallFactory); 116 } 117 118 private void generateDecl(Declaration tree) { 119 try { 120 tree.accept(this, null); 121 } catch (Exception ex) { 122 ex.printStackTrace(); 123 } 124 } 125 126 private JavaFileObject fileFromString(String pkgName, String clsName, String contents) { 127 String pkgPrefix = pkgName.isEmpty() ? "" : pkgName.replaceAll("\\.", "/") + "/"; 128 return new SimpleJavaFileObject(URI.create(pkgPrefix + clsName + ".java"), JavaFileObject.Kind.SOURCE) { 129 @Override 130 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 131 return contents; 132 } 133 }; 134 } 135 136 @Override 137 public Void visitVariable(Declaration.Variable tree, Declaration parent) { 138 String fieldName = tree.name(); 139 String symbol = tree.name(); 140 assert !symbol.isEmpty(); 141 assert !fieldName.isEmpty(); 142 Type type = tree.type(); 143 MemoryLayout layout = tree.layout().orElse(Type.layoutFor(type).orElse(null)); 144 if (layout == null) { 145 //no layout - abort 146 return null; 147 } 148 Class<?> clazz = typeTranslator.getJavaType(type); 149 if (clazz == MemoryAddress.class || clazz == MemorySegment.class || layout.byteSize() > 8) { 150 //skip 151 return null; 152 } 153 154 if (parent != null) { 155 //struct field 156 builder.addVarHandle(fieldName, clazz, parent.name()); 157 } else { 158 builder.addLayout(fieldName, layout); 159 builder.addVarHandle(fieldName, clazz, null); 160 builder.addAddress(fieldName); 161 } 162 163 return null; 164 } 165 166 @Override 167 public Void visitFunction(Declaration.Function funcTree, Declaration parent) { 168 FunctionDescriptor descriptor = Type.descriptorFor(funcTree.type()).orElse(null); 169 if (descriptor == null) { 170 //abort 171 } 172 MethodType mtype = typeTranslator.getMethodType(funcTree.type()); 173 builder.addMethodHandle(funcTree, mtype, descriptor); 174 return null; 175 } 176 177 @Override 178 public Void visitConstant(Declaration.Constant constant, Declaration parent) { 179 if (!constants.add(constant.name())) { 180 //skip 181 return null; 182 } 183 184 builder.addConstant(constant.name(), typeTranslator.getJavaType(constant.type()), constant.value()); 185 return null; 186 } 187 188 @Override 189 public Void visitScoped(Declaration.Scoped d, Declaration parent) { 190 if (d.kind() == Declaration.Scoped.Kind.TYPEDEF) { 191 return d.members().get(0).accept(this, d); 192 } 193 if (d.layout().isEmpty()) { 194 //skip decl-only 195 return null; 196 } 197 String name = d.name(); 198 if (d.name().isEmpty() && parent != null) { 199 name = parent.name(); 200 } 201 202 if (!d.name().isEmpty() || !isRecord(parent)) { 203 //only add explicit struct layout if the struct is not to be flattened inside another struct 204 switch (d.kind()) { 205 case STRUCT: 206 case UNION: 207 builder.addLayout(name, d.layout().get()); 208 break; 209 } 210 } 211 d.members().forEach(fieldTree -> fieldTree.accept(this, d.name().isEmpty() ? parent : d)); 212 return null; 213 } 214 215 private boolean isRecord(Declaration declaration) { 216 if (declaration == null) { 217 return false; 218 } else if (!(declaration instanceof Declaration.Scoped)) { 219 return false; 220 } else { 221 Declaration.Scoped scope = (Declaration.Scoped)declaration; 222 return scope.kind() == Declaration.Scoped.Kind.CLASS || 223 scope.kind() == Declaration.Scoped.Kind.STRUCT || 224 scope.kind() == Declaration.Scoped.Kind.UNION; 225 } 226 } 227 }