aboutsummaryrefslogblamecommitdiff
path: root/src/main/java/org/glavo/javah/Utils.java
blob: b3554ff969896b2a3346244109a499d0f3fc1382 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12

                        

                              







                                 

             

















































































                                                                                                          














































                                                                              
                                                                                    








































































                                                                                                                 
package org.glavo.javah;

import org.objectweb.asm.Type;

import java.io.IOException;
import java.io.InputStream;
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;

class Utils {
    static String encode(String str) {
        StringBuilder buffer = new StringBuilder(str.length());

        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')) {
                buffer.append(ch);
            } else {
                buffer.append(String.format("_0%04x", (int) ch));
            }
        }
        return buffer.toString();
    }

    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";
        }

        if (jt.toString().startsWith("[")) {
            return "jobjectArray";
        }

        return "jobject";
    }

    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");
        }
        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;
        }
        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);
    }

    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;
            }
        }
        return null;
    }

    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();
            }
        }
        if (!Files.isDirectory(root)) {
            return Collections.emptyList();
        }

        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()
                );

            } catch (Exception ignored) {
            }
        }

        if (!isMultiRelease) {
            return Collections.singletonList(root);
        }
        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);

        } catch (IOException ignored) {
        }

        list.add(root);
        return list;
    }

    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;
            }
        }
        return false;
    }

    static Path searchFrom(List<Path> searchPaths, String name) {
        Objects.requireNonNull(searchPaths);
        Objects.requireNonNull(name);

        name = simpleNameOf(name).replace('.', '/') + ".class";

        for (Path searchPath : searchPaths) {
            Path p = searchPath.resolve(name);
            if (Files.isRegularFile(p)) {
                return p;
            }
        }
        return null;
    }
}