diff options
Diffstat (limited to 'src/main/java/org/glavo/javah/Utils.java')
-rw-r--r-- | src/main/java/org/glavo/javah/Utils.java | 271 |
1 files changed, 92 insertions, 179 deletions
diff --git a/src/main/java/org/glavo/javah/Utils.java b/src/main/java/org/glavo/javah/Utils.java index e9043ea..cb1ceef 100644 --- a/src/main/java/org/glavo/javah/Utils.java +++ b/src/main/java/org/glavo/javah/Utils.java @@ -1,221 +1,134 @@ package org.glavo.javah; -import org.objectweb.asm.Type; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; import java.io.IOException; -import java.io.InputStream; +import java.io.PrintWriter; +import java.io.Writer; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; -import java.util.jar.Manifest; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; class Utils { - static String encode(String str) { - StringBuilder buffer = new StringBuilder(str.length()); + public static final int MAX_SUPPORTED_VERSION = 13; - int len = str.length(); - for (int i = 0; i < len; i++) { - char ch = str.charAt(i); - if (ch == '_') { - buffer.append("_1"); - } else if (ch == ';') { - buffer.append("_2"); - } else if (ch == '[') { - buffer.append("_3"); - } else if ((ch >= '0' && ch <= '9') - || (ch >= 'a' && ch <= 'z') - || (ch >= 'A' && ch <= 'Z') - || ch == '/' - || ch == '.') { - buffer.append(ch); - } else { - buffer.append(String.format("_0%04x", (int) ch)); - } - } - return buffer.toString(); - } + public static final List<String> MULTI_RELEASE_VERSIONS = + IntStream.rangeClosed(9, MAX_SUPPORTED_VERSION).mapToObj(Integer::toString).collect(Collectors.toList()); - static String mapToNativeType(Type jt) { - switch (jt.toString()) { - case "Z": - return "jboolean"; - case "B": - return "jbyte"; - case "C": - return "jchar"; - case "S": - return "jshort"; - case "I": - return "jint"; - case "J": - return "jlong"; - case "F": - return "jfloat"; - case "D": - return "jdouble"; - case "V": - return "void"; - case "Ljava/lang/Class;": - return "jclass"; - case "Ljava/lang/String;": - return "jstring"; - case "Ljava/lang/Throwable ;": - return "jthrowable"; - case "[Z": - return "jbooleanArray"; - case "[B": - return "jbyteArray"; - case "[C": - return "jcharArray"; - case "[S": - return "jshortArray"; - case "[I": - return "jintArray"; - case "[J": - return "jlongArray"; - case "[F": - return "jfloatArray"; - case "[D": - return "jdoubleArray"; - } + public static final Pattern SIMPLE_NAME_PATTERN = Pattern.compile("[^.;\\[/]+"); + public static final Pattern FULL_NAME_PATTERN = + Pattern.compile("[^.;\\[/]+(\\.[^.;\\[/]+)*"); - if (jt.toString().startsWith("[")) { - return "jobjectArray"; - } - - return "jobject"; - } + public static final Pattern METHOD_NAME_PATTERN = Pattern.compile("(<init>)|(<cinit>)|([^.;\\[/<>]+)"); + public static final Pattern METHOD_TYPE_PATTERN = + Pattern.compile("\\((?<args>(\\[*([BCDFIJSZ]|L[^.;\\[/]+(/[^.;\\\\\\[/]+)*;))*)\\)(?<ret>\\[*([BCDFIJSZV]|L[^.;\\[/]+(/[^.;\\[/]+)*;))"); - static String mangledArgSignature(Type methodType) { - Objects.requireNonNull(methodType); - String str = methodType.toString(); - int idx = str.indexOf(')'); - if (idx == -1) { - throw new IllegalArgumentException(methodType.toString() + "is not a method type"); + public static final PrintWriter NOOP_WRITER = new PrintWriter(new Writer() { + @Override + public void write(char[] cbuf, int off, int len) throws IOException { } - return encode(str.substring(1, idx)); - } - - static boolean isRegularClassName(String name) { - Objects.requireNonNull(name); - return !name.contains("//") - && !name.contains("..") - && name.indexOf('\\') == -1 - && name.indexOf(';') == -1 - && name.indexOf('[') == -1; - } - static String fullClassNameOf(String name) { - Objects.requireNonNull(name); - int idx = name.indexOf('/'); - if (idx == -1) { - return name; + @Override + public void flush() throws IOException { } - String cn = name.substring(idx + 1); - if (cn.isEmpty() || cn.indexOf('/') == -1) { - throw new IllegalArgumentException("Illegal class name: " + name); - } - return cn; - } - static String simpleNameOf(String name) { - int idx = name.lastIndexOf('.'); - return name.substring(idx + 1); - } + @Override + public void close() throws IOException { + } + }); - static Path searchFromPathList(List<Path> paths, String className) { - Objects.requireNonNull(paths); - Objects.requireNonNull(className); - String fp = fullClassNameOf(className).replace('.', '/') + ".class"; - for (Path path : paths) { - Objects.requireNonNull(path); - Path p = path.resolve(fp); - if (Files.isRegularFile(p)) { - return p; + public static String mangleName(String name) { + StringBuilder builder = new StringBuilder(name.length() * 2); + int len = name.length(); + for (int i = 0; i < len; i++) { + char ch = name.charAt(i); + if (ch == '.') { + builder.append('_'); + } else if (ch == '_') { + builder.append("_1"); + } else if (ch == ';') { + builder.append("_2"); + } else if (ch == '[') { + builder.append("_3"); + } else if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && (ch <= 'Z'))) { + builder.append(ch); + } else { + builder.append(String.format("_0%04x", (int) ch)); } } - return null; + return builder.toString(); } - static List<Path> getPathsFrom(Path root) { - Objects.requireNonNull(root); - root = root.toAbsolutePath(); - if (Files.isRegularFile(root)) { - String name = root.toString().toLowerCase(); - try { - FileSystem fs = FileSystems.newFileSystem(root, (ClassLoader) null); - if (name.endsWith(".jar")) { - root = fs.getPath("/"); - } else if (name.endsWith(".jmod")) { - root = fs.getPath("/classes"); - } else { - return Collections.emptyList(); - } - } catch (IOException e) { - return Collections.emptyList(); + public static String escape(String unicode) { + Objects.requireNonNull(unicode); + int len = unicode.length(); + StringBuilder builder = new StringBuilder(len); + for (int i = 0; i < len; i++) { + char ch = unicode.charAt(i); + if (ch >= ' ' && ch <= '~') { + builder.append(ch); + } else { + builder.append(String.format("\\u%04x", (int) ch)); } } - if (!Files.isDirectory(root)) { - return Collections.emptyList(); - } + return builder.toString(); + } - boolean isMultiRelease = false; - Path manifest = root.resolve("META-INF").resolve("MANIFEST.MF"); - if (Files.exists(manifest) && Files.isRegularFile(manifest)) { - try (InputStream input = Files.newInputStream(manifest)) { - isMultiRelease = Boolean.parseBoolean( - new Manifest(input).getMainAttributes().getOrDefault("Multi-Release", "false").toString() - ); + public static Path classPathRoot(Path p) { + Objects.requireNonNull(p); + p = p.toAbsolutePath(); - } catch (Exception ignored) { - } + if (Files.notExists(p)) { + return null; } - - if (!isMultiRelease) { - return Collections.singletonList(root); + if (Files.isDirectory(p)) { + return p; } - LinkedList<Path> list = new LinkedList<>(); - Path mr = root.resolve("META-INF").resolve("versions"); + try { - Files.list(mr) - .filter(Utils::checkMultiReleaseVersion) - .sorted(Comparator.comparingInt(a -> Integer.parseInt(a.getFileName().toString()))) - .forEachOrdered(list::addFirst); + FileSystem fs = FileSystems.newFileSystem(p, (ClassLoader) null); + String name = p.getFileName().toString().toLowerCase(); + if (name.endsWith(".jar") || name.endsWith(".zip")) { + return fs.getPath("/"); + } + if (name.endsWith(".jmod")) { + return fs.getPath("/", "classes"); + } + fs.close(); } catch (IOException ignored) { + return null; } - - list.add(root); - return list; + return null; } - private static final String[] MULTI_VERSIONS = {"9", "10", "11", "12", "13"}; - - private static boolean checkMultiReleaseVersion(Path p) { - Objects.requireNonNull(p); - String n = p.getFileName().toString(); - for (String version : MULTI_VERSIONS) { - if (version.equals(n)) { - return true; + public static ClassName superClassOf(ClassReader reader) { + Objects.requireNonNull(reader); + class V extends ClassVisitor { + V() { + super(Opcodes.ASM7); } - } - return false; - } - - static Path searchFrom(List<Path> searchPaths, String name) { - Objects.requireNonNull(searchPaths); - Objects.requireNonNull(name); - name = simpleNameOf(name).replace('.', '/') + ".class"; + ClassName superName = null; - for (Path searchPath : searchPaths) { - Path p = searchPath.resolve(name); - if (Files.isRegularFile(p)) { - return p; + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (superName != null) { + this.superName = ClassName.of(superName.replace('/', '.')); + } } } - return null; + V v = new V(); + reader.accept(v, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); + return v.superName; } } |