From 0b52aa7a9a2d8cc9ffb77d5f81f3e2f366fce622 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 5 May 2018 19:04:04 +0800 Subject: add main --- src/main/java/org/glavo/javah/HeaderGenerator.java | 251 ++++++++++++++++++--- 1 file changed, 218 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/glavo/javah/HeaderGenerator.java b/src/main/java/org/glavo/javah/HeaderGenerator.java index 31eeef6..81fe6c5 100644 --- a/src/main/java/org/glavo/javah/HeaderGenerator.java +++ b/src/main/java/org/glavo/javah/HeaderGenerator.java @@ -3,14 +3,25 @@ package org.glavo.javah; import org.objectweb.asm.*; import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; +import java.nio.file.*; import java.util.*; public final class HeaderGenerator { private static final Path[] EMPTY_PATH_ARRAY = new Path[0]; private static final List THROWABLE_NAME_LIST = Arrays.asList("Ljava/lang/Throwable;", "Ljava/lang/Error;", "Ljava/lang/Exception"); private static final HeaderGenerator generator = new HeaderGenerator(); + public static final String VERSION = "0.1.1"; + private static final String message = "Usage: \n" + + " javah [options] \n" + + "where [options] include:\n" + + " -o Output file (only one of -d or -o may be used)\n" + + " -d Output directory\n" + + " -h -help --help -? Print this message\n" + + " -version Print version information\n" + + " -classpath Path from which to load classes\n" + + " -cp Path from which to load classes\n" + + " are specified with their fully qualified names\n" + + "(for example, java.lang.Object)."; private static String escape(String source) { StringBuilder builder = new StringBuilder(); @@ -106,28 +117,7 @@ public final class HeaderGenerator { return true; } try { - ClassReader reader = null; - loop: - for (Path path : classPaths) { - if (!Files.exists(path)) { - continue; - } - String[] ps = (className + ".class").split("/"); - for (String p : ps) { - path = path.resolve(p); - if (!Files.exists(path)) { - continue loop; - } - } - try { - reader = new ClassReader(Files.newInputStream(path)); - break; - } catch (IOException ignored) { - } - } - if (reader == null && useRuntimeClassPath) { - reader = new ClassReader(className.replace('/', '.')); - } + ClassReader reader = findClass(className); if (reader == null) { return false; } @@ -180,6 +170,36 @@ public final class HeaderGenerator { } } + private ClassReader findClass(String name) { + loop: + for (Path path : classPaths) { + path = path.toAbsolutePath(); + if (!Files.exists(path)) { + continue; + } + String[] ps = name.split("/|\\."); + if (ps.length == 0) { + continue; + } + ps[ps.length - 1] += ".class"; + for (String p : ps) { + path = path.resolve(p); + if (!Files.exists(path)) { + continue loop; + } + } + try { + return new ClassReader(Files.newInputStream(path)); + } catch (IOException ignored) { + } + } + try { + return new ClassReader(name.replace('/', '.')); + } catch (IOException e) { + return null; + } + } + private void classGenerateFunctionDeclarations(Generator generator, PrintWriter output) { String className = escape(generator.getClassName()); @@ -230,13 +250,7 @@ public final class HeaderGenerator { classGenerateFunctionDeclarations(generator, output); } - public void classGenerateHeader(ClassReader reader, PrintWriter output) { - output.println("/* DO NOT EDIT THIS FILE - it is machine generated */"); - output.println("#include "); - - Generator generator = new Generator(); - reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); - + private void classGenerateHeaderWithoutInclude(Generator generator, PrintWriter output) { StringBuilder builder = new StringBuilder(); String className = escape(generator.getClassName()); @@ -256,7 +270,25 @@ public final class HeaderGenerator { "}\n" + "#endif\n" + "#endif\n"); + } + + private void classGenerateHeaderWithoutInclude(ClassReader reader, PrintWriter output) { + Generator generator = new Generator(); + reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + classGenerateHeaderWithoutInclude(generator, output); + } + + private void classGenerateHeader(Generator generator, PrintWriter output) { + output.println("/* DO NOT EDIT THIS FILE - it is machine generated */"); + output.println("#include "); + classGenerateHeaderWithoutInclude(generator, output); + } + + public void classGenerateHeader(ClassReader reader, PrintWriter output) { + Generator generator = new Generator(); + reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + classGenerateHeader(generator, output); } public void classGenerateHeader(byte[] classFile, PrintWriter output) { @@ -268,7 +300,11 @@ public final class HeaderGenerator { } public void classGenerateHeader(String className, PrintWriter output) throws IOException { - classGenerateHeader(new ClassReader(className), output); + ClassReader reader = findClass(className); + if (reader == null) { + throw new IOException(); + } + classGenerateHeader(reader, output); } public void classGenerateHeader(InputStream input, PrintWriter output) throws IOException { @@ -293,6 +329,154 @@ public final class HeaderGenerator { } public static void main(String[] args) throws IOException { + boolean isWindows = System.getProperty("os.name", "").toLowerCase().contains("windows"); + ArrayList cps = new ArrayList<>(); + Path outputFile = null; + Path outputDir = null; + ArrayList classNames = new ArrayList<>(); + + if (args.length == 0) { + System.out.println(message); + return; + } + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (arg.startsWith("-")) { + switch (arg) { + case "-h": + case "-help": + case "--help": + case "-?": + System.out.println(message); + return; + case "-version": + System.out.println(); + return; + case "-cp": + case "-classpath": + i++; + if (i == args.length) { + System.err.println("javah: " + arg + " requires an argument."); + return; + } + String[] s = args[i].split(isWindows ? ";" : ":"); + for (String ss : s) { + Path p = Paths.get(ss); + if (Files.exists(p)) { + if (Files.isDirectory(p)) { + cps.add(p); + } else if (ss.toLowerCase().endsWith(".jar") || ss.toLowerCase().endsWith(".zip")) { + try { + cps.add(FileSystems.newFileSystem(p, null).getPath("/")); + } catch (Exception ignored) { + } + } + } + } + break; + case "-o": + i++; + if (i == args.length) { + System.err.println("javah: " + arg + " requires an argument."); + return; + } + if (outputDir != null) { + System.err.println("Error: Can't mix options -d and -o. Try -help."); + return; + } + outputFile = Paths.get(args[i]); + break; + + case "-d": + i++; + if (i == args.length) { + System.err.println("javah: " + arg + " requires an argument."); + return; + } + if (outputFile != null) { + System.err.println("Error: Can't mix options -d and -o. Try -help."); + return; + } + outputDir = Paths.get(args[i]); + + break; + default: + System.err.println("Error: unknown option: " + arg); + return; + } + + } else { + if (arg.contains("/")) { + int idx = arg.indexOf('/'); + if (idx != arg.lastIndexOf('/')) { + System.err.println("Not a valid class name: " + arg); + } + arg = arg.substring(idx + 1); + } + classNames.add(arg); + } + + if (outputDir == null && outputFile == null) { + outputDir = Paths.get(System.getProperty("user.dir", ".")); + } + if (cps.isEmpty()) { + cps.add(Paths.get(System.getProperty("user.dir", "."))); + } + + if (outputDir != null && Files.notExists(outputDir)) { + Files.createDirectories(outputDir); + } + + if (outputFile != null && Files.notExists(outputFile)) { + if (Files.notExists(outputFile.getParent())) { + Files.createDirectories(outputFile.getParent()); + } + } + + HeaderGenerator generator = new HeaderGenerator(cps.toArray(new Path[0])); + if (outputFile != null) { + if (Files.notExists(outputFile.getParent())) { + Files.createDirectories(outputFile.getParent()); + } + try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) { + boolean first = true; + for (String name : classNames) { + ClassReader reader = generator.findClass(name); + if (reader == null) { + System.err.println("Error: Could not find class file for '" + name + "'."); + return; + } + Generator g = new Generator(); + reader.accept(g, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + if (first) { + generator.classGenerateHeader(g, writer); + first = false; + } else { + generator.classGenerateHeaderWithoutInclude(g, writer); + } + } + } + } + if (outputDir != null) { + if (Files.notExists(outputDir)) { + Files.createDirectories(outputDir); + } + for (String name : classNames) { + ClassReader reader = generator.findClass(name); + if (reader == null) { + System.err.println("Error: Could not find class file for '" + name + "'."); + return; + } + Generator g = new Generator(); + reader.accept(g, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + + try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputDir.resolve(name.replace('.', '_') + ".h"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) { + generator.classGenerateHeader(g, writer); + } + } + } + + } } } @@ -309,6 +493,7 @@ class MethodDesc { class Generator extends ClassVisitor { private String className; + private Map constants = new LinkedHashMap<>(); private Map> methods = new LinkedHashMap>(); public Generator() { @@ -326,7 +511,7 @@ class Generator extends ClassVisitor { if (methods.containsKey(name)) { methods.get(name).add(new MethodDesc((access & Opcodes.ACC_STATIC) != 0, descriptor)); } else { - LinkedHashSet set = new LinkedHashSet(); + LinkedHashSet set = new LinkedHashSet<>(); set.add(new MethodDesc((access & Opcodes.ACC_STATIC) != 0, descriptor)); methods.put(name, set); } -- cgit v1.2.3