--- old/src/java.base/share/classes/module-info.java 2019-04-24 19:38:27.000000000 +0530 +++ new/src/java.base/share/classes/module-info.java 2019-04-24 19:38:27.000000000 +0530 @@ -167,8 +167,7 @@ jdk.jartool, jdk.jfr, jdk.jlink, - jdk.scripting.nashorn, - jdk.jextract; + jdk.scripting.nashorn; exports jdk.internal.org.objectweb.asm.tree to jdk.jfr, jdk.jlink; --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/JModWriter.java 2019-04-24 19:38:29.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/JModWriter.java 2019-04-24 19:38:28.000000000 +0530 @@ -29,19 +29,16 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.spi.ToolProvider; import java.util.logging.Logger; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.ModuleVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; // Utility class to generate a .jmod file public final class JModWriter { - private final Options options; - private final Log log; + private final Context ctx; private final Writer writer; private static ToolProvider findTool(String name) { @@ -53,20 +50,20 @@ return tp.get(); } + private static final ToolProvider JAVAC = findTool("javac"); private static final ToolProvider JMOD = findTool("jmod"); public JModWriter(Context ctx, Writer writer) { - this.options = ctx.options; - this.log = ctx.log; + this.ctx = ctx; this.writer = writer; } public void writeJModFile(Path jmodFile, String[] args) throws IOException { - if (options.targetPackage == null || options.targetPackage.isEmpty()) { + if (ctx.options.targetPackage == null || ctx.options.targetPackage.isEmpty()) { throw new IllegalArgumentException("no --target-package specified"); } - log.print(Level.INFO, () -> "Collecting jmod file " + jmodFile); + ctx.log.print(Level.INFO, () -> "Collecting jmod file " + jmodFile); String modName = jmodFile.getFileName().toString(); modName = modName.substring(0, modName.length() - 5 /* ".jmod".length() */); @@ -75,14 +72,14 @@ Path jmodRootDir = Files.createTempDirectory("jextract.jmod"); jmodRootDir.toFile().deleteOnExit(); - log.print(Level.INFO, () -> "Writing .class files"); + ctx.log.print(Level.INFO, () -> "Writing .class files"); // write .class files Path modClassesDir = jmodRootDir.resolve(modName); writer.writeClassFiles(modClassesDir, args); - log.print(Level.INFO, () -> "Generating module-info.class"); + ctx.log.print(Level.INFO, () -> "Generating module-info.class"); // generate module-info.class - generateModuleInfoClass(modClassesDir, modName); + generateModuleInfoClass(jmodRootDir, modClassesDir, modName); // copy libraries Path libsDir = jmodRootDir.resolve("libs"); @@ -92,40 +89,50 @@ generateJMod(modClassesDir, libsDir, jmodFile); } - private void generateModuleInfoClass(Path modClassesDir, String modName) throws IOException { + private void generateModuleInfoClass(Path jmodRootDir, Path modClassesDir, String modName) throws IOException { // collect package names final Set packages = new HashSet<>(); for (String cls : writer.results().keySet()) { - int idx = cls.lastIndexOf("/"); + int idx = cls.lastIndexOf("."); packages.add(cls.substring(0, idx)); } - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - cw.visit(Opcodes.V9, Opcodes.ACC_MODULE, "module-info", null, null, null); - - ModuleVisitor mv = cw.visitModule(modName, Opcodes.ACC_MANDATED, null); - mv.visitRequire("java.base", Opcodes.ACC_MANDATED, null); + // module-info.java source code string + StringBuilder modInfoCode = new StringBuilder(); + modInfoCode.append("module "); + modInfoCode.append(modName); + modInfoCode.append(" {\n"); for (String pkg : packages) { - mv.visitExport(pkg, Opcodes.ACC_MANDATED); - } - mv.visitEnd(); - - cw.visitEnd(); + modInfoCode.append(" exports "); + modInfoCode.append(pkg); + modInfoCode.append(";\n"); + } + modInfoCode.append("}"); + + // write module-info.java source in module directory + Files.write(modClassesDir.resolve("module-info.java"), List.of(modInfoCode.toString())); + + // compile module-info.java + int exitCode = JAVAC.run(ctx.log.getOut(), ctx.log.getErr(), + "--module-source-path", jmodRootDir.toString(), + "-d", jmodRootDir.toString(), + modClassesDir.resolve("module-info.java").toString()); - // write module-info.class source in module directory - Files.write(modClassesDir.resolve("module-info.class"), cw.toByteArray()); + if (exitCode != 0) { + throw new RuntimeException("module-info.class generation failed: " + exitCode); + } } private void copyNativeLibraries(Path libsDir) throws IOException { Files.createDirectory(libsDir); - if (!options.libraryNames.isEmpty()) { - if (options.libraryPaths.isEmpty()) { - log.printWarning("warn.no.library.paths.specified"); + if (!ctx.options.libraryNames.isEmpty()) { + if (ctx.options.libraryPaths.isEmpty()) { + ctx.log.printWarning("warn.no.library.paths.specified"); return; } - log.print(Level.INFO, () -> "Copying native libraries"); - Path[] paths = options.libraryPaths.stream().map(Paths::get).toArray(Path[]::new); - options.libraryNames.forEach(libName -> { + ctx.log.print(Level.INFO, () -> "Copying native libraries"); + Path[] paths = ctx.options.libraryPaths.stream().map(Paths::get).toArray(Path[]::new); + ctx.options.libraryNames.forEach(libName -> { Optional absPath = Utils.findLibraryPath(paths, libName); if (absPath.isPresent()) { Path libPath = absPath.get(); @@ -135,7 +142,7 @@ throw new UncheckedIOException(ioExp); } } else { - log.printWarning("warn.library.not.copied", libName); + ctx.log.printWarning("warn.library.not.copied", libName); } }); } @@ -143,8 +150,8 @@ private void generateJMod(Path classesDir, Path libsDir, Path jmodFile) throws IOException { - log.print(Level.INFO, () -> "Generating jmod file: " + jmodFile); - int exitCode = JMOD.run(log.getOut(), log.getErr(), "create", + ctx.log.print(Level.INFO, () -> "Generating jmod file: " + jmodFile); + int exitCode = JMOD.run(ctx.log.getOut(), ctx.log.getErr(), "create", "--class-path", classesDir.toString(), "--libs", libsDir.toString(), jmodFile.toString()); --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/JType.java 2019-04-24 19:38:30.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/JType.java 2019-04-24 19:38:29.000000000 +0530 @@ -22,16 +22,10 @@ */ package com.sun.tools.jextract; -import jdk.internal.org.objectweb.asm.ClassVisitor; import java.foreign.memory.Callback; import java.foreign.memory.Pointer; import java.util.Objects; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; - /** * A Java Type descriptor */ @@ -44,7 +38,6 @@ */ public abstract String getDescriptor(); - public void visitInner(ClassVisitor cv) {} public String getSignature(boolean isArgument) { return getDescriptor(); } @@ -183,14 +176,6 @@ return clsName; } } - - @Override - public void visitInner(ClassVisitor cv) { - if (enclosingName != null) { - cv.visitInnerClass(clsName, enclosingName, simpleName, - ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); - } - } } public final static class ArrayType extends JType { @@ -206,11 +191,6 @@ } @Override - public void visitInner(ClassVisitor cv) { - elementType.visitInner(cv); - } - - @Override public String getSignature(boolean isArgument) { StringBuilder sb = new StringBuilder(); sb.append("L"); @@ -282,14 +262,6 @@ } @Override - public void visitInner(ClassVisitor cv) { - returnType.visitInner(cv); - for (JType at : args) { - at.visitInner(cv); - } - } - - @Override public String getSignature(boolean isArgument) { StringBuilder sb = new StringBuilder(); sb.append('('); @@ -321,12 +293,6 @@ Function getFunction() { return fn; } - - @Override - public void visitInner(ClassVisitor cv) { - fn.visitInner(cv); - super.visitInner(cv); - } } public static class GenericType extends ClassType { @@ -383,11 +349,5 @@ public static GenericType ofCallback(JType targ) { return new GenericType(JType.binaryName(Callback.class), targ); } - - @Override - public void visitInner(ClassVisitor cv) { - targ.visitInner(cv); - super.visitInner(cv); - } } } --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/JarWriter.java 2019-04-24 19:38:31.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/JarWriter.java 2019-04-24 19:38:30.000000000 +0530 @@ -54,7 +54,7 @@ public void writeJarFile(JarOutputStream jar, String[] args) { writer.results().forEach((cls, bytes) -> { try { - String path = cls.replace('.', File.separatorChar) + ".class"; + String path = cls.replace('.', '/') + ".class"; log.print(Level.FINE, () -> "Add " + path); jar.putNextEntry(new ZipEntry(path)); jar.write(bytes); --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/JavaSourceFactory.java 2019-04-24 19:38:32.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/JavaSourceFactory.java 2019-04-24 19:38:31.000000000 +0530 @@ -90,12 +90,13 @@ this.libraryPaths = ctx.options.recordLibraryPath? ctx.options.libraryPaths : null; this.noNativeLocations = ctx.options.noNativeLocations; this.global_jsb = new JavaSourceBuilder(); - this.srcDir = Paths.get(ctx.options.srcDumpDir) - .resolve(headerFile.pkgName.replace('.', File.separatorChar)); + this.srcDir = ctx.options.srcDumpDir != null? + Paths.get(ctx.options.srcDumpDir).resolve(headerFile.pkgName.replace('.', File.separatorChar)) : + null; } // main entry point that generates & saves .java files for the header file - public void generate(List decls) { + public Map generate(List decls) { global_jsb.addPackagePrefix(headerFile.pkgName); Map header = new HashMap<>(); @@ -141,13 +142,19 @@ global_jsb.interfaceEnd(); String src = global_jsb.build(); - try { - Files.createDirectories(srcDir); - Path srcPath = srcDir.resolve(clsName + ".java"); - Files.write(srcPath, List.of(src)); - } catch (Exception ex) { - handleException(ex); + if (srcDir != null) { + try { + Files.createDirectories(srcDir); + Path srcPath = srcDir.resolve(clsName + ".java"); + Files.write(srcPath, List.of(src)); + } catch (Exception ex) { + handleException(ex); + } } + + Map srcMap = new HashMap<>(); + srcMap.put(headerClassName, src); + return srcMap; } protected void handleException(Exception ex) { --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/JavaSourceFactoryExt.java 2019-04-24 19:38:33.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/JavaSourceFactoryExt.java 2019-04-24 19:38:33.000000000 +0530 @@ -25,7 +25,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Level; @@ -58,7 +60,7 @@ } @Override - public void generate(List decls) { + public Map generate(List decls) { header_jsb.addPackagePrefix(headerFile.pkgName); String ifaceClsName = headerFile.headerClsName; String forwarderName = headerFile.staticForwarderClsName; @@ -67,17 +69,21 @@ header_jsb.addLibraryField(ifaceClsName, STATICS_LIBRARY_FIELD_NAME); header_jsb.emitScopeAccessor(STATICS_LIBRARY_FIELD_NAME); - super.generate(decls); + Map srcMap = super.generate(decls); enums.forEach(header_jsb::addNestedType); header_jsb.classEnd(); String src = header_jsb.build(); - try { - Path srcPath = srcDir.resolve(forwarderName + ".java"); - Files.write(srcPath, List.of(src)); - } catch (Exception ex) { - handleException(ex); + if (srcDir != null) { + try { + Path srcPath = srcDir.resolve(forwarderName + ".java"); + Files.write(srcPath, List.of(src)); + } catch (Exception ex) { + handleException(ex); + } } + srcMap.put(headerFile.pkgName + "." + forwarderName, src); + return srcMap; } @Override --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/JextractTool.java 2019-04-24 19:38:34.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/JextractTool.java 2019-04-24 19:38:34.000000000 +0530 @@ -32,6 +32,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -44,7 +45,6 @@ public class JextractTool { private final HeaderResolver headerResolver; private final Parser parser; - private final Function codeFactory; private final Collection clangArgs; private final Collection sources; private final Context ctx; @@ -52,9 +52,6 @@ public JextractTool(Context ctx) { this.headerResolver = new HeaderResolver(ctx); this.parser = new Parser(ctx, Options.INCLUDE_MACROS); - this.codeFactory = ctx.options.genStaticForwarder ? - hf -> new AsmCodeFactoryExt(ctx, hf) : - hf -> new AsmCodeFactory(ctx, hf); this.clangArgs = ctx.options.clangArgs; this.sources = ctx.sources; this.ctx = ctx; @@ -77,21 +74,18 @@ .collect(Collectors.groupingBy(this::headerFromDecl)); //generate classes - Map results = new LinkedHashMap<>(); - headerMap.forEach((hf, decls) -> generateHeader(hf, decls, results)); - return new Writer(ctx, results); + Map srcMap = new HashMap<>(); + headerMap.forEach((hf, decls) -> generateHeader(hf, decls,srcMap)); + Map classMap = !srcMap.isEmpty()? InMemoryJavaCompiler.compile(srcMap) : Map.of(); + return new Writer(ctx, classMap); } - private void generateHeader(HeaderFile hf, List decls, Map results) { + private void generateHeader(HeaderFile hf, List decls, Map srcMap) { TypeEnter enter = new TypeEnter(hf.dictionary()); decls.forEach(t -> t.accept(enter, null)); - if (ctx.options.srcDumpDir != null) { - JavaSourceFactory jsb = ctx.options.genStaticForwarder ? - new JavaSourceFactoryExt(ctx, hf) : new JavaSourceFactory(ctx, hf); - jsb.generate(decls); - } - AsmCodeFactory cf = codeFactory.apply(hf); - results.putAll(cf.generateNativeHeader(decls)); + JavaSourceFactory jsb = ctx.options.genStaticForwarder ? + new JavaSourceFactoryExt(ctx, hf) : new JavaSourceFactory(ctx, hf); + srcMap.putAll(jsb.generate(decls)); } private HeaderFile headerFromDecl(Tree tree) { --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/Writer.java 2019-04-24 19:38:35.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/Writer.java 2019-04-24 19:38:35.000000000 +0530 @@ -41,7 +41,7 @@ this.results = results; } - static final String JEXTRACT_MANIFEST = "META-INF" + File.separatorChar + "jextract.properties"; + static final String JEXTRACT_MANIFEST = "META-INF/jextract.properties"; public boolean isEmpty() { return results.isEmpty(); --- old/src/jdk.jextract/share/classes/module-info.java 2019-04-24 19:38:36.000000000 +0530 +++ new/src/jdk.jextract/share/classes/module-info.java 2019-04-24 19:38:36.000000000 +0530 @@ -24,7 +24,7 @@ */ module jdk.jextract { - requires java.compiler; + requires jdk.compiler; requires java.logging; requires jdk.internal.opt; requires jdk.internal.clang; --- old/test/jdk/com/sun/tools/jextract/Runner.java 2019-04-24 19:38:37.000000000 +0530 +++ new/test/jdk/com/sun/tools/jextract/Runner.java 2019-04-24 19:38:37.000000000 +0530 @@ -299,14 +299,13 @@ actualCL = new ClassLoader() { @Override protected Class findClass(String name) throws ClassNotFoundException { - byte[] byteCode = actualClz.get(canonicalize(name)); + byte[] byteCode = actualClz.get(name); if (byteCode == null) throw new ClassNotFoundException(name); return defineClass(name, byteCode, 0, byteCode.length); } }; System.out.println("Done compile, ready for test"); assertEquals(actualClz.keySet().stream() - .map(Runner::normalize) .collect(Collectors.toSet()), mfm.listClasses()); System.out.println("Compile result validated."); @@ -316,10 +315,6 @@ return classname.replace('/', '.'); } - private static String canonicalize(String classname) { - return classname.replace('.', '/'); - } - private static Path[] paths(String testDir, String[] files, boolean platformDependent) { boolean isWindows = System.getProperty("os.name").startsWith("Windows"); return Arrays.stream(files) --- /dev/null 2019-04-24 19:38:38.000000000 +0530 +++ new/src/jdk.jextract/share/classes/com/sun/tools/jextract/InMemoryJavaCompiler.java 2019-04-24 19:38:38.000000000 +0530 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.tools.jextract; + +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; + +final class InMemoryJavaCompiler { + private InMemoryJavaCompiler() {} + + static Map compile(Map inputMap, + String... options) { + Collection sourceFiles = new LinkedList<>(); + for (Entry entry : inputMap.entrySet()) { + sourceFiles.add(new SourceFile(entry.getKey(), entry.getValue())); + } + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + FileManager fileManager = new FileManager(compiler.getStandardFileManager(null, null, null)); + + Writer writer = new StringWriter(); + Boolean exitCode = compiler.getTask(writer, fileManager, null, Arrays.asList(options), null, sourceFiles).call(); + if (!exitCode) { + throw new RuntimeException("Test bug: in memory compilation failed: " + writer.toString()); + } + return fileManager.getByteCode(); + } + + // Wraper for class byte array + private static class ClassFile extends SimpleJavaFileObject { + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + protected ClassFile(String name) { + super(URI.create("memo:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS); + } + + @Override + public ByteArrayOutputStream openOutputStream() { return this.baos; } + + byte[] toByteArray() { return baos.toByteArray(); } + } + + // File manager which spawns ClassFile instances on demand + private static class FileManager extends ForwardingJavaFileManager { + private final Map classesMap = new HashMap<>(); + + protected FileManager(JavaFileManager fileManager) { + super(fileManager); + } + + @Override + public ClassFile getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) { + ClassFile classFile = new ClassFile(name); + classesMap.put(name, classFile); + return classFile; + } + + public Map getByteCode() { + Map result = new HashMap<>(); + for (Entry entry : classesMap.entrySet()) { + result.put(entry.getKey(), entry.getValue().toByteArray()); + } + return result; + } + } + + // Wrapper for source String + private static class SourceFile extends SimpleJavaFileObject { + private final CharSequence sourceCode; + + public SourceFile(String name, CharSequence sourceCode) { + super(URI.create("memo:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); + this.sourceCode = sourceCode; + } + + @Override + public CharSequence getCharContent(boolean ignore) { + return this.sourceCode; + } + } +} --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/AsmCodeFactory.java 2019-04-24 19:38:39.000000000 +0530 +++ /dev/null 2019-04-24 19:38:39.000000000 +0530 @@ -1,490 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.sun.tools.jextract; - -import java.foreign.layout.Layout; -import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.sun.tools.jextract.parser.MacroParser; -import jdk.internal.clang.SourceLocation; -import jdk.internal.clang.Type; -import jdk.internal.org.objectweb.asm.AnnotationVisitor; -import jdk.internal.org.objectweb.asm.ClassVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import com.sun.tools.jextract.tree.EnumTree; -import com.sun.tools.jextract.tree.FieldTree; -import com.sun.tools.jextract.tree.FunctionTree; -import com.sun.tools.jextract.tree.MacroTree; -import com.sun.tools.jextract.tree.SimpleTreeVisitor; -import com.sun.tools.jextract.tree.StructTree; -import com.sun.tools.jextract.tree.Tree; -import com.sun.tools.jextract.tree.TypedefTree; -import com.sun.tools.jextract.tree.VarTree; - -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ANNOTATION; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; -import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.I2B; -import static jdk.internal.org.objectweb.asm.Opcodes.I2C; -import static jdk.internal.org.objectweb.asm.Opcodes.I2S; -import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; - -/** - * Scan a header file and generate classes for entities defined in that header - * file. Tree visitor visit methods return true/false depending on whether a - * particular Tree is processed or skipped. - */ -class AsmCodeFactory extends SimpleTreeVisitor { - private static final String ANNOTATION_PKG_PREFIX = "Ljava/foreign/annotations/"; - private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;"; - private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;"; - private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;"; - private static final String NATIVE_STRUCT = ANNOTATION_PKG_PREFIX + "NativeStruct;"; - private static final String NATIVE_FUNCTION = ANNOTATION_PKG_PREFIX + "NativeFunction;"; - private static final String NATIVE_GETTER = ANNOTATION_PKG_PREFIX + "NativeGetter;"; - private static final String NATIVE_SETTER = ANNOTATION_PKG_PREFIX + "NativeSetter;"; - private static final String NATIVE_ADDRESSOF = ANNOTATION_PKG_PREFIX + "NativeAddressof;"; - private static final String NATIVE_NUM_CONST = ANNOTATION_PKG_PREFIX + "NativeNumericConstant;"; - private static final String NATIVE_STR_CONST = ANNOTATION_PKG_PREFIX + "NativeStringConstant;"; - - private final ClassWriter global_cw; - private final Set global_layouts = new LinkedHashSet<>(); - protected final String headerClassName; - protected final HeaderFile headerFile; - protected final Map types; - protected final List libraryNames; - protected final List libraryPaths; - protected final boolean noNativeLocations; - - protected final Log log; - - AsmCodeFactory(Context ctx, HeaderFile header) { - this.log = ctx.log; - log.print(Level.INFO, () -> "Instantiate AsmCodeFactory for " + header.path); - this.headerFile = header; - this.headerClassName = Utils.toInternalName(headerFile.pkgName, headerFile.headerClsName); - this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - this.types = new HashMap<>(); - this.libraryNames = ctx.options.libraryNames; - this.libraryPaths = ctx.options.recordLibraryPath? ctx.options.libraryPaths : null; - this.noNativeLocations = ctx.options.noNativeLocations; - global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, - headerClassName, - null, "java/lang/Object", null); - } - - public Map generateNativeHeader(List decls) { - //generate all decls - decls.forEach(this::generateDecl); - //generate functional interfaces - headerFile.dictionary().functionalInterfaces() - .forEach(fi -> createFunctionalInterface((JType.FunctionalInterfaceType)fi)); - - //generate header intf - AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true); - av.visit("path", headerFile.path.toAbsolutePath().toString()); - if (!libraryNames.isEmpty()) { - AnnotationVisitor libNames = av.visitArray("libraries"); - for (String name : libraryNames) { - libNames.visit(null, name); - } - libNames.visitEnd(); - if (libraryPaths != null && !libraryPaths.isEmpty()) { - AnnotationVisitor libPaths = av.visitArray("libraryPaths"); - for (String path : libraryPaths) { - libPaths.visit(null, path); - } - libPaths.visitEnd(); - } - } - - AnnotationVisitor resolutionContext = av.visitArray("resolutionContext"); - headerFile.dictionary().resolutionRoots() - .forEach(jt -> resolutionContext.visit(null, - jdk.internal.org.objectweb.asm.Type.getObjectType(jt.clsName))); - resolutionContext.visitEnd(); - AnnotationVisitor globals = av.visitArray("globals"); - global_layouts.stream().map(Layout::toString).forEach(s -> globals.visit(null, s)); - globals.visitEnd(); - av.visitEnd(); - global_cw.visitEnd(); - addClassIfNeeded(headerClassName, global_cw.toByteArray()); - return Collections.unmodifiableMap(types); - } - - private void handleException(Exception ex) { - log.printError("cannot.write.class.file", headerFile.pkgName + "." + headerFile.headerClsName, ex); - log.printStackTrace(ex); - } - - private void annotateNativeLocation(ClassVisitor cw, Tree tree) { - if (! noNativeLocations) { - AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true); - SourceLocation src = tree.location(); - SourceLocation.Location loc = src.getFileLocation(); - Path p = loc.path(); - av.visit("file", p == null ? "" : p.toAbsolutePath().toString()); - av.visit("line", loc.line()); - av.visit("column", loc.column()); - av.visitEnd(); - } - } - - private void addClassIfNeeded(String clsName, byte[] bytes) { - if (null != types.put(clsName, bytes)) { - log.printWarning("warn.class.overwritten", clsName); - } - } - - private static boolean isBitField(Tree tree) { - return tree instanceof FieldTree && ((FieldTree)tree).isBitField(); - } - - /** - * - * @param cw ClassWriter for the struct - * @param tree The Tree - * @param parentType The struct type - */ - private boolean addField(ClassVisitor cw, Tree tree, Type parentType) { - String fieldName = tree.name(); - assert !fieldName.isEmpty(); - Type type = tree.type(); - JType jt = headerFile.dictionary().lookup(type); - assert (jt != null); - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get", - "()" + jt.getDescriptor(), "()" + jt.getSignature(false), null); - jt.visitInner(cw); - - if (! noNativeLocations) { - AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); - SourceLocation src = tree.location(); - SourceLocation.Location loc = src.getFileLocation(); - Path p = loc.path(); - av.visit("file", p == null ? "" : p.toAbsolutePath().toString()); - av.visit("line", loc.line()); - av.visit("column", loc.column()); - av.visitEnd(); - } - - AnnotationVisitor av = mv.visitAnnotation(NATIVE_GETTER, true); - av.visit("value", fieldName); - av.visitEnd(); - - mv.visitEnd(); - mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set", - "(" + jt.getDescriptor() + ")V", - "(" + jt.getSignature(true) + ")V", null); - jt.visitInner(cw); - av = mv.visitAnnotation(NATIVE_SETTER, true); - av.visit("value", fieldName); - av.visitEnd(); - mv.visitEnd(); - - if (tree instanceof VarTree || !isBitField(tree)) { - JType ptrType = JType.GenericType.ofPointer(jt); - mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr", - "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(false), null); - ptrType.visitInner(cw); - av = mv.visitAnnotation(NATIVE_ADDRESSOF, true); - av.visit("value", fieldName); - av.visitEnd(); - mv.visitEnd(); - } - - return true; - } - - @Override - public Boolean visitVar(VarTree varTree, JType jt) { - global_layouts.add(varTree.layout().withAnnotation(Layout.NAME, varTree.name())); - return addField(global_cw, varTree, null); - } - - private void addConstant(ClassWriter cw, SourceLocation src, String name, JType type, Object value) { - String desc = "()" + type.getDescriptor(); - String sig = "()" + type.getSignature(false); - MethodVisitor mv = global_cw.visitMethod(ACC_ABSTRACT | ACC_PUBLIC, name, desc, sig, null); - type.visitInner(cw); - - if (! noNativeLocations) { - AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); - SourceLocation.Location loc = src.getFileLocation(); - Path p = loc.path(); - av.visit("file", p == null ? "" : p.toAbsolutePath().toString()); - av.visit("line", loc.line()); - av.visit("column", loc.column()); - av.visitEnd(); - } - - if (value instanceof String) { - AnnotationVisitor av = mv.visitAnnotation(NATIVE_STR_CONST, true); - av.visit("value", value); - av.visitEnd(); - } else { - //numeric (int, long or double) - final long longValue; - if (value instanceof Integer) { - longValue = (Integer)value; - } else if (value instanceof Long) { - longValue = (Long)value; - } else if (value instanceof Double) { - longValue = Double.doubleToRawLongBits((Double)value); - } else { - throw new IllegalStateException("Unexpected constant: " + value); - } - AnnotationVisitor av = mv.visitAnnotation(NATIVE_NUM_CONST, true); - av.visit("value", longValue); - av.visitEnd(); - } - mv.visitEnd(); - } - - @Override - public Boolean visitStruct(StructTree structTree, JType jt) { - //generate nested structs recursively - structTree.nestedTypes().forEach(this::generateDecl); - - if (structTree.isAnonymous()) { - //skip anonymous - return false; - } - String nativeName = structTree.name(); - Type type = structTree.type(); - log.print(Level.FINE, () -> "Create struct: " + nativeName); - - String intf = Utils.toClassName(nativeName); - String name = headerClassName + "$" + intf; - - log.print(Level.FINE, () -> "Define class " + name + " for native type " + nativeName); - /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not - * taking static as a valid flag, so comment this out during development. - */ - global_cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT, - name, "Ljava/lang/Object;Ljava/foreign/memory/Struct;", - "java/lang/Object", new String[] {"java/foreign/memory/Struct"}); - annotateNativeLocation(cw, structTree); - - AnnotationVisitor av = cw.visitAnnotation(NATIVE_STRUCT, true); - Layout structLayout = structTree.layout(); - av.visit("value", structLayout.toString()); - av.visitEnd(); - cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); - - // fields - structTree.fields().forEach(fieldTree -> addField(cw, fieldTree, type)); - // Write class - cw.visitEnd(); - addClassIfNeeded(headerClassName + "$" + intf, cw.toByteArray()); - return true; - } - - @Override - public Boolean visitEnum(EnumTree enumTree, JType jt) { - // define enum constants in global_cw - enumTree.constants().forEach(constant -> addConstant(global_cw, - constant.location(), - constant.name(), - headerFile.dictionary().lookup(constant.type()), - constant.enumConstant().get())); - - if (enumTree.name().isEmpty()) { - // We are done with anonymous enum - return true; - } - - // generate annotation class for named enum - createAnnotationCls(enumTree); - return true; - } - - private void createAnnotationCls(Tree tree) { - String nativeName = tree.name(); - log.print(Level.FINE, () -> "Create annotation for: " + nativeName); - - String intf = Utils.toClassName(nativeName); - String name = headerClassName + "$" + intf; - - log.print(Level.FINE, () -> "Define class " + name + " for native type " + nativeName); - global_cw.visitInnerClass(name, headerClassName, intf, - ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - String[] superAnno = { "java/lang/annotation/Annotation" }; - cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION, - name, null, "java/lang/Object", superAnno); - annotateNativeLocation(cw, tree); - AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true); - av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE"); - av.visitEnd(); - av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); - av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); - av.visitEnd(); - cw.visitInnerClass(name, headerClassName, intf, - ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); - // Write class - cw.visitEnd(); - addClassIfNeeded(headerClassName + "$" + intf, cw.toByteArray()); - } - - private void createFunctionalInterface(JType.FunctionalInterfaceType fnif) { - JType.Function fn = fnif.getFunction(); - String intf; - String nativeName; - String nDesc = fnif.getFunction().getNativeDescriptor(); - intf = fnif.getSimpleName(); - nativeName = "anonymous function"; - log.print(Level.FINE, () -> "Create FunctionalInterface " + intf); - - final String name = headerClassName + "$" + intf; - - log.print(Level.FINE, () -> "Define class " + name + " for native type " + nativeName + nDesc); - global_cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, - name, "Ljava/lang/Object;", - "java/lang/Object", new String[0]); - AnnotationVisitor av = cw.visitAnnotation( - "Ljava/lang/FunctionalInterface;", true); - av.visitEnd(); - av = cw.visitAnnotation(NATIVE_CALLBACK, true); - av.visit("value", nDesc); - av.visitEnd(); - cw.visitInnerClass(name, headerClassName, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); - - // add the method - - int flags = ACC_PUBLIC | ACC_ABSTRACT; - if (fn.isVarArgs) { - flags |= ACC_VARARGS; - } - MethodVisitor mv = cw.visitMethod(flags, "fn", - fn.getDescriptor(), fn.getSignature(false), null); - fn.visitInner(cw); - mv.visitEnd(); - // Write class - cw.visitEnd(); - addClassIfNeeded(headerClassName + "$" + intf, cw.toByteArray()); - } - - @Override - public Boolean visitTypedef(TypedefTree typedefTree, JType jt) { - createAnnotationCls(typedefTree); - return true; - } - - @Override - public Boolean visitTree(Tree tree, JType jt) { - log.print(Level.WARNING, () -> "Unsupported declaration tree:"); - log.print(Level.WARNING, () -> tree.toString()); - return true; - } - - @Override - public Boolean visitFunction(FunctionTree funcTree, JType jt) { - assert (jt instanceof JType.Function); - JType.Function fn = (JType.Function)jt; - log.print(Level.FINE, () -> "Add method: " + fn.getSignature(false)); - int flags = ACC_PUBLIC | ACC_ABSTRACT; - if (fn.isVarArgs) { - flags |= ACC_VARARGS; - } - MethodVisitor mv = global_cw.visitMethod(flags, - funcTree.name(), fn.getDescriptor(), fn.getSignature(false), null); - jt.visitInner(global_cw); - final int arg_cnt = funcTree.numParams(); - for (int i = 0; i < arg_cnt; i++) { - String name = funcTree.paramName(i); - final int tmp = i; - log.print(Level.FINER, () -> " arg " + tmp + ": " + name); - mv.visitParameter(name, 0); - } - - if (! noNativeLocations) { - AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); - SourceLocation src = funcTree.location(); - SourceLocation.Location loc = src.getFileLocation(); - Path p = loc.path(); - av.visit("file", p == null ? "" : p.toAbsolutePath().toString()); - av.visit("line", loc.line()); - av.visit("column", loc.column()); - av.visitEnd(); - } - - Type type = funcTree.type(); - final String descStr = Utils.getFunction(type).toString(); - - AnnotationVisitor av = mv.visitAnnotation(NATIVE_FUNCTION, true); - av.visit("value", descStr); - av.visitEnd(); - - mv.visitEnd(); - return true; - } - - private AsmCodeFactory generateDecl(Tree tree) { - try { - log.print(Level.FINE, () -> "Process tree " + tree.name()); - tree.accept(this, tree.isPreprocessing() ? null : headerFile.dictionary().lookup(tree.type())); - } catch (Exception ex) { - handleException(ex); - log.print(Level.WARNING, () -> "Tree causing above exception is: " + tree.name()); - log.print(Level.WARNING, () -> tree.toString()); - } - return this; - } - - @Override - public Boolean visitMacro(MacroTree macroTree, JType jt) { - if (!macroTree.isConstant()) { - log.print(Level.FINE, () -> "Skipping unrecognized object-like macro " + macroTree.name()); - return false; - } - String name = macroTree.name(); - MacroParser.Macro macro = macroTree.macro().get(); - log.print(Level.FINE, () -> "Adding macro " + name); - - addConstant(global_cw, macroTree.location(), Utils.toMacroName(name), macro.type(), macro.value()); - - return true; - } -} --- old/src/jdk.jextract/share/classes/com/sun/tools/jextract/AsmCodeFactoryExt.java 2019-04-24 19:38:40.000000000 +0530 +++ /dev/null 2019-04-24 19:38:40.000000000 +0530 @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.sun.tools.jextract; - -import java.foreign.Libraries; -import java.foreign.Scope; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.MethodType; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.logging.Level; - -import com.sun.tools.jextract.parser.MacroParser; -import com.sun.tools.jextract.tree.Tree; -import jdk.internal.org.objectweb.asm.FieldVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Type; -import com.sun.tools.jextract.tree.EnumTree; -import com.sun.tools.jextract.tree.FieldTree; -import com.sun.tools.jextract.tree.FunctionTree; -import com.sun.tools.jextract.tree.MacroTree; -import com.sun.tools.jextract.tree.VarTree; - -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; -import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST; -import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; - -/** - * This extended factory generates a class with only static methods and fields. A native - * library interface instance (of the given header file) is kept as a static private field. - * One static method is generated for every library interface method. Enum and macro constants - * are mapped to static final fields. By importing the "static forwarder" class, the user code - * looks more or less like C code. Libraries.bind and header interface usage is hidden. - */ -final class AsmCodeFactoryExt extends AsmCodeFactory { - private final String headerClassNameDesc; - private final String forwarderClassName; - private final ClassWriter cw; - // suffix for static forwarder class name - // field name for the header interface instance. - private static final String STATICS_LIBRARY_FIELD_NAME = "_theLibrary"; - - private final List> constantInitializers = new ArrayList<>(); - private final List enumFactories = new ArrayList<>(); - - AsmCodeFactoryExt(Context ctx, HeaderFile header) { - super(ctx, header); - log.print(Level.INFO, () -> "Instantiate StaticForwarderGenerator for " + header.path); - this.headerClassNameDesc = "L" + headerClassName + ";"; - this.forwarderClassName = Utils.toInternalName(header.pkgName, header.staticForwarderClsName); - this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - this.cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL, getClassName(), - null, "java/lang/Object", null); - scopeAccessor(); - } - - private class EnumFactory { - private final EnumTree enumTree; - private final String enumClassName; - - EnumFactory(EnumTree enumTree) { - log.print(Level.INFO, () -> "Instantiate EnumFactory for " + enumTree.name()); - this.enumTree = enumTree; - this.enumClassName = AsmCodeFactoryExt.this.getClassName() + "$" + enumTree.name(); - - } - - String getEnumName() { - return enumTree.name(); - } - - String getClassName() { - return enumClassName; - } - - byte[] getClassBytes() { - return generate(); - } - - private byte[] generate() { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, enumClassName, - null, "java/lang/Object", null); - cw.visitInnerClass(enumClassName, AsmCodeFactoryExt.this.getClassName(), enumTree.name(), ACC_PUBLIC | ACC_FINAL); - enumTree.constants().forEach(constant -> { - String name = constant.name(); - JType type = headerFile.dictionary().lookup(constant.type()); - Object constantValue = makeConstantValue(type, constant.enumConstant().get()); - FieldVisitor fv = cw.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, name, type.getDescriptor(), - type.getSignature(false), constantValue); - fv.visitEnd(); - }); - - cw.visitEnd(); - return cw.toByteArray(); - } - } - - @Override - public Boolean visitVar(VarTree varTree, JType jt) { - if (super.visitVar(varTree, jt)) { - String fieldName = varTree.name(); - assert !fieldName.isEmpty(); - - emitStaticForwarder(fieldName + "$get", - "()" + jt.getDescriptor(), "()" + jt.getSignature(false), false); - jt.visitInner(cw); - - emitStaticForwarder(fieldName + "$set", - "(" + jt.getDescriptor() + ")V", - "(" + jt.getSignature(true) + ")V", false); - JType ptrType = JType.GenericType.ofPointer(jt); - emitStaticForwarder(fieldName + "$ptr", - "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(false), false); - ptrType.visitInner(cw); - - return true; - } else { - return false; - } - } - - @Override - public Boolean visitEnum(EnumTree enumTree, JType jt) { - if (super.visitEnum(enumTree, jt)) { - if (enumTree.name().isEmpty()) { - enumTree.constants().forEach(constant -> addConstant(constant.name(), - headerFile.dictionary().lookup(constant.type()), - constant.enumConstant().get())); - } else { - EnumFactory ef = new EnumFactory(enumTree); - enumFactories.add(ef); - cw.visitInnerClass(ef.getClassName(), getClassName(), ef.getEnumName(), - ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); - } - return true; - } else { - return false; - } - } - - @Override - public Boolean visitFunction(FunctionTree funcTree, JType jt) { - if (super.visitFunction(funcTree, jt)) { - assert (jt instanceof JType.Function); - JType.Function fn = (JType.Function)jt; - log.print(Level.FINE, () -> "Add method: " + fn.getSignature(false)); - emitStaticForwarder(funcTree.name(), fn.getDescriptor(), fn.getSignature(false), fn.isVarArgs); - fn.visitInner(cw); - return true; - } else { - return false; - } - } - - @Override - public Boolean visitMacro(MacroTree macroTree, JType jt) { - if (super.visitMacro(macroTree, jt)) { - String name = macroTree.name(); - MacroParser.Macro macro = macroTree.macro().get(); - log.print(Level.FINE, () -> "Adding macro " + name); - addConstant(Utils.toMacroName(name), macro.type(), macro.value()); - return true; - } else { - return false; - } - } - - private void addConstant(String name, JType type, Object value) { - Object constantValue = makeConstantValue(type, value); - FieldVisitor fv = cw.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, name, type.getDescriptor(), - type.getSignature(false), constantValue); - fv.visitEnd(); - if (constantValue == null) { - constantInitializers.add(mv -> { - // load library interface (static) field - String desc = type.getDescriptor(); - mv.visitFieldInsn(GETSTATIC, getClassName(), - STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); - mv.visitMethodInsn(INVOKEINTERFACE, headerClassName, name, "()" + desc, true); - mv.visitFieldInsn(PUTSTATIC, getClassName(), name, desc); - }); - } - } - - private Object makeConstantValue(JType type, Object value) { - switch (type.getDescriptor()) { - case "Z": - return ((long)value) != 0; - case "C": - return (char)(long)value; - case "B": - return (byte)(long)value; - case "S": - return (short)(long)value; - case "I": - return (int)(long)value; - case "F": - return (float)(double)value; - case "J": case "D": - return value; - default: - return null; - } - } - - @Override - public Map generateNativeHeader(List decls) { - Map results = new HashMap<>(); - results.putAll(super.generateNativeHeader(decls)); - staticsInitializer(); - results.put(getClassName(), getClassBytes()); - for (EnumFactory ef : enumFactories) { - results.put(ef.getClassName(), ef.getClassBytes()); - } - return Collections.unmodifiableMap(results); - } - - // Internals only below this point - - private String getClassName() { - return forwarderClassName; - } - - // return the generated static forwarder class bytes - private byte[] getClassBytes() { - cw.visitEnd(); - return cw.toByteArray(); - } - - // emit library interface static field and initializer for that field - private void staticsInitializer() { - // library interface field - FieldVisitor fv = cw.visitField(ACC_PRIVATE|ACC_STATIC|ACC_FINAL, - STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc, null, null); - fv.visitEnd(); - - // to bind library interface field - MethodVisitor mv = cw.visitMethod(ACC_STATIC, - "", "()V", null, null); - mv.visitCode(); - - // MethodHandles.lookup() - Method lookupMethod = null; - try { - lookupMethod = MethodHandles.class.getMethod("lookup"); - } catch (NoSuchMethodException nsme) { - throw new RuntimeException(nsme); - } - mv.visitMethodInsn(INVOKESTATIC, - Type.getInternalName(MethodHandles.class), "lookup", - Type.getMethodDescriptor(lookupMethod), false); - - // ldc library-interface-class - mv.visitLdcInsn(Type.getObjectType(headerClassName)); - - // Libraries.bind(lookup, class); - Method bindMethod = null; - try { - bindMethod = Libraries.class.getMethod("bind", Lookup.class, Class.class); - } catch (NoSuchMethodException nsme) { - throw new RuntimeException(nsme); - } - mv.visitMethodInsn(INVOKESTATIC, - Type.getInternalName(Libraries.class), "bind", - Type.getMethodDescriptor(bindMethod), false); - - // store it in library interface field - mv.visitTypeInsn(CHECKCAST, headerClassName); - mv.visitFieldInsn(PUTSTATIC, getClassName(), - STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); - - constantInitializers.forEach(init -> init.accept(mv)); - - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - private void scopeAccessor() { - String scopeAccessorDesc = MethodType.methodType(Scope.class).descriptorString(); - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "scope", scopeAccessorDesc, null, null); - mv.visitCode(); - - // load library interface (static) field - mv.visitFieldInsn(GETSTATIC, getClassName(), - STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); - - String libraryScopeDesc = MethodType.methodType(Scope.class, Object.class).descriptorString(); - mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Libraries.class), "libraryScope", libraryScopeDesc, false); - mv.visitInsn(ARETURN); - mv.visitMaxs(1,1); - mv.visitEnd(); - } - - // emit static forwarder method for a specific library interface method - private void emitStaticForwarder(String name, String desc, String signature, boolean isVarArgs) { - int accessFlags = ACC_PUBLIC | ACC_STATIC; - if (isVarArgs) { - accessFlags |= ACC_VARARGS; - } - - MethodVisitor mv = cw.visitMethod(accessFlags, name, desc, signature, null); - mv.visitCode(); - - // load library interface (static) field - mv.visitFieldInsn(GETSTATIC, getClassName(), - STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); - - // forward the call to the interface - Type[] argTypes = Type.getArgumentTypes(desc); - Type retType = Type.getReturnType(desc); - - int loadIdx = 0; - for (int i = 0; i < argTypes.length; i++) { - mv.visitVarInsn(argTypes[i].getOpcode(ILOAD), loadIdx); - loadIdx += argTypes[i].getSize(); - } - mv.visitMethodInsn(INVOKEINTERFACE, headerClassName, name, desc, true); - mv.visitInsn(retType.getOpcode(IRETURN)); - - mv.visitMaxs(0, 0); - mv.visitEnd(); - } -}