All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.d2j.tools.jar.InitOut Maven / Gradle / Ivy

package com.googlecode.d2j.tools.jar;

import com.googlecode.dex2jar.tools.BaseCmd;
import com.googlecode.dex2jar.tools.Constants;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;

import static com.googlecode.d2j.util.AccUtils.isEnum;
import static com.googlecode.d2j.util.AccUtils.isFinal;
import static com.googlecode.d2j.util.AccUtils.isPrivate;
import static com.googlecode.d2j.util.AccUtils.isProtected;
import static com.googlecode.d2j.util.AccUtils.isPublic;
import static com.googlecode.d2j.util.AccUtils.isStatic;
import static com.googlecode.d2j.util.AccUtils.isSynthetic;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.LDC;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.PUTSTATIC;

public class InitOut {

    private static final Set KEYWORDS = new HashSet<>(Arrays.asList("abstract", "continue", "for", "new",
            "switch", "assert", "default", "goto", "package", "synchronized", "boolean", "do", "if", "private", "this",
            "break", "double", "implements", "protected", "throw", "byte", "else", "import", "public", "throws",
            "case", "enum", "instanceof", "return", "transient", "catch", "extends", "int", "short", "try", "char",
            "final", "interface", "static", "void", "class", "finally", "long", "strictfp", "volatile", "const",
            "float", "native", "super", "while"));

    private int clzIndex = 0;

    private final Set clzMap = new TreeSet<>();

    private final Set clzSet = new TreeSet<>();

    private Path from;

    private int maxLength = 40;

    private final Set memberMap = new TreeSet<>();

    private int minLength = 2;

    private int pkgIndex = 0;

    private final Set pkgMap = new TreeSet<>();

    private final Set pkgSet = new TreeSet<>();

    private boolean initEnumNames = false;

    private boolean initSourceNames = false;

    private boolean initAssertionNames = false;

    public InitOut initEnumNames() {
        this.initEnumNames = true;
        return this;
    }

    public InitOut initSourceNames() {
        this.initSourceNames = true;
        return this;
    }

    public InitOut initAssertionNames() {
        this.initAssertionNames = true;
        return this;
    }

    private void doClass0(String clz) {
        if (clzSet.contains(clz)) {
            return;
        }
        clzSet.add(clz);

        int index = clz.lastIndexOf('$');
        if (index > 0) {
            doClass0(clz.substring(0, index));
            String cName = clz.substring(index + 1);
            try {
                Integer.parseInt(cName);
            } catch (Exception ex) {
                if (shouldRename(cName)) {
                    clzMap.add(String.format("c %s=CI%03d%s", clz, clzIndex++, short4LongName(cName)));
                }
            }
        } else {
            index = clz.lastIndexOf('/');
            if (index > 0) {
                doPkg(clz.substring(0, index));
                String cName = clz.substring(index + 1);
                if (shouldRename(cName)) {
                    clzMap.add(String.format("c %s=C%03d%s", clz, clzIndex++, short4LongName(cName)));
                }
            } else {
                if (shouldRename(clz)) {
                    clzMap.add(String.format("c %s=CI_%03d%s", clz, clzIndex++, short4LongName(clz)));
                }
            }
        }
    }

    private String short4LongName(String name) {
        if (name.length() > maxLength) {
            return "x" + Integer.toHexString(name.hashCode());
        } else {
            return name;
        }
    }

    private void doMember(String owner, MemberInfo member, final int x) {
        // TODO use suggested name
        if (x > 0 || shouldRename(member.name)) {
            if (member.desc.indexOf('(') >= 0) {
                StringBuilder sb = new StringBuilder();
                sb.append(isStatic(member.access) ? "M" : "m");
                if (isPrivate(member.access)) {
                    sb.append("p");
                } else if (isPublic(member.access)) {
                    sb.append("P");
                }
                if (x > 0) {
                    sb.append(x);
                }
                sb.append(short4LongName(member.name));
                if (x > 0) {
                    memberMap.add("m " + owner + "." + member.name + member.desc + "=" + sb);
                } else {
                    memberMap.add("m " + owner + "." + member.name
                            + member.desc.substring(0, member.desc.indexOf(')') + 1) + "=" + sb);
                }
            } else {
                StringBuilder sb = new StringBuilder();
                sb.append(isStatic(member.access) ? "F" : "f");
                if (isPrivate(member.access)) {
                    sb.append("p");
                } else if (isPublic(member.access)) {
                    sb.append("P");
                }
                if (x > 0) {
                    sb.append(x);
                }
                sb.append(short4LongName(member.name));
                if (x > 0) {
                    memberMap.add("m " + owner + "." + member.name + "[" + member.desc + "]" + "=" + sb);
                } else {
                    memberMap.add("m " + owner + "." + member.name + "=" + sb);
                }
            }
        }
    }

    private List collect(Path file) throws IOException {
        final List clzList = new ArrayList<>();
        final ClassVisitor collectVisitor = new ClassVisitor(Constants.ASM_VERSION) {
            private static final String ASSERTION_DISABLED_FIELD_NAME = "$assertionsDisabled";
            private static final String ENUM_VALUES_FIELD_NAME = "ENUM$VALUES";
            ClassInfo clz;
            boolean isEnum = false;
            final Map enumFieldMap = new HashMap<>();

            @Override
            public void visitSource(String source, String debug) {
                if (initSourceNames && !clz.name.contains("$") && source.endsWith(".java")) {
                    clz.suggestName = source.substring(0, source.length() - 5);
                }
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature,
                                             String[] exceptions) {
                clz.addMethod(access, name, desc);
                if (initEnumNames && isEnum && name.equals("")) {
                    final String thisDesc = "L" + clz.name + ";";
                    return new MethodNode(Constants.ASM_VERSION, access, name, desc, signature, exceptions) {
                        @Override
                        public void visitEnd() {
                            if (this.instructions != null) {
                                int status = 0;
                                String eFieldName = null;
                                for (AbstractInsnNode p = this.instructions.getFirst(); p != null; p = p.getNext()) {
                                    //looking for  NEW,DUP,LDC,PUTSTATUS

                                    if (status == 0) {  // init
                                        if (p.getOpcode() == NEW) {
                                            TypeInsnNode ti = (TypeInsnNode) p;
                                            if (thisDesc.equals(ti.desc)) {
                                                status = 1;
                                            }
                                        }
                                    } else if (status == 1) {  // find NEW
                                        if (p.getOpcode() == DUP) {
                                            status = 2;
                                        } else {
                                            status = 0;
                                        }
                                    } else if (status == 2) {  //find DUP
                                        if (p.getOpcode() == LDC) {
                                            LdcInsnNode ldc = (LdcInsnNode) p;
                                            if (ldc.cst instanceof String) {
                                                eFieldName = (String) ldc.cst;
                                                status = 3;
                                            } else {
                                                status = 0;
                                            }
                                        } else {
                                            status = 0;
                                        }
                                    } else {  //find LDC
                                        if (p.getOpcode() == PUTSTATIC) {
                                            FieldInsnNode fin = (FieldInsnNode) p;
                                            if (fin.owner.equals(thisDesc) && fin.desc.equals(thisDesc)) {
                                                if (!fin.name.equals(eFieldName)) {
                                                    enumFieldMap.put(fin.name, eFieldName);
                                                }
                                                eFieldName = null;
                                                status = 0;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    };
                }
                return null;
            }

            @Override
            public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
                MemberInfo mi = clz.addField(access, name, desc);
                if (initEnumNames && isEnum
                        && isPrivate(access) && isFinal(access) && isSynthetic(access)
                        && !ENUM_VALUES_FIELD_NAME.equals(name)
                        && ("[L" + clz.name + ";").equals(desc)) {
                    mi.suggestName = ENUM_VALUES_FIELD_NAME;
                }
                if (initAssertionNames && isSynthetic(access) && isStatic(access)
                        && !isPrivate(access) && !isPublic(access) && !isProtected(access)
                        && desc.equals("Z") && !ASSERTION_DISABLED_FIELD_NAME.equals(name)) {
                    mi.suggestName = ASSERTION_DISABLED_FIELD_NAME;
                }

                return null;
            }

            @Override
            public void visitEnd() {
                if (initEnumNames) {
                    final String thisDesc = "L" + clz.name + ";";
                    for (Map.Entry e : enumFieldMap.entrySet()) {
                        String name = e.getKey();
                        String suggestName = e.getValue();
                        for (MemberInfo mi : clz.fields) {
                            if (isFinal(mi.access) && isStatic(mi.access)
                                    && mi.name.equals(name) && mi.desc.equals(thisDesc)) {
                                mi.suggestName = suggestName;
                            }
                        }
                    }
                }
                clzList.add(clz);
                clz = null;
                isEnum = false;
                enumFieldMap.clear();
            }

            @Override
            public void visit(int version, int access, String name, String signature, String superName,
                              String[] interfaces) {
                this.clz = new ClassInfo(name);
                isEnum = isEnum(access);
            }
        };

        try (FileSystem fs = BaseCmd.openZip(file)) {
            BaseCmd.walkJarOrDir(fs.getPath("/"), (file1, relative) -> {
                if (relative.endsWith(".class")) {
                    byte[] data = Files.readAllBytes(file1);
                    new ClassReader(data).accept(collectVisitor, ClassReader.EXPAND_FRAMES);
                }
            });
        }
        return clzList;
    }

    private void doPkg(String pkg) {
        if (pkgSet.contains(pkg)) {
            return;
        }
        pkgSet.add(pkg);
        int index = pkg.lastIndexOf('/');
        if (index > 0) {
            doPkg(pkg.substring(0, index));
            String cName = pkg.substring(index + 1);
            if (shouldRename(cName)) {
                pkgMap.add(String.format("p %s=p%02d%s", pkg, pkgIndex++, short4LongName(cName)));
            }
        } else {
            if (shouldRename(pkg)) {
                pkgMap.add(String.format("p %s=p%02d%s", pkg, pkgIndex++, short4LongName(pkg)));
            }
        }
    }

    public InitOut from(Path from) {
        this.from = from;
        return this;
    }

    public InitOut maxLength(int m) {
        this.maxLength = m;
        return this;
    }

    public InitOut minLength(int m) {
        this.minLength = m;
        return this;
    }

    private boolean shouldRename(String s) {
        return s.length() > maxLength || s.length() < minLength || KEYWORDS.contains(s);
    }

    public void to(Path config) throws IOException {
        List classInfoList = collect(from);
        transform(classInfoList);
        List list = new ArrayList<>();
        list.addAll(pkgMap);
        list.addAll(clzMap);
        list.addAll(memberMap);
        Files.write(config, list, StandardCharsets.UTF_8);
    }

    private void transformerMember(String owner, List members) {
        Iterator it = members.iterator();
        if (it.hasNext()) {
            MemberInfo current = it.next();
            while (true) {
                if (it.hasNext()) {
                    MemberInfo next = it.next();
                    if (current.name.equals(next.name)) {
                        int x = 1;
                        doMember(owner, current, x++);
                        doMember(owner, next, x++);
                        while (it.hasNext()) {
                            next = it.next();
                            if (current.name.equals(next.name)) {
                                doMember(owner, next, x++);
                            } else {
                                current = next;
                                break;
                            }
                        }
                    } else {
                        doMember(owner, current, 0);
                        current = next;
                    }
                } else {
                    doMember(owner, current, 0);
                    break;
                }
            }
        }
    }

    private void transform(List classInfoList) {
        MemberInfoComparator comparator = new MemberInfoComparator();
        for (ClassInfo ci : classInfoList) {
            doClass0(ci.name);
            ci.fields.sort(comparator);
            transformerMember(ci.name, ci.fields);
            ci.methods.sort(comparator);
            transformerMember(ci.name, ci.methods);
        }

    }

    private static class ClassInfo {

        public final String name;

        public List fields = new ArrayList<>(5);

        public List methods = new ArrayList<>(5);

        public String suggestName;

        ClassInfo(String name) {
            this.name = name;
        }

        public MemberInfo addField(int access, String name, String type) {
            MemberInfo mi = new MemberInfo(access, name, type);
            fields.add(mi);
            return mi;
        }

        public MemberInfo addMethod(int access, String name, String desc) {
            MemberInfo mi = new MemberInfo(access, name, desc);
            methods.add(mi);
            return mi;
        }

        public String toString() {
            return name;
        }

    }

    private static class MemberInfo {

        public int access;

        public String desc;

        public String name;

        public String suggestName;

        MemberInfo(int access, String name, String desc) {
            this.name = name;
            this.access = access;
            this.desc = desc;
        }

    }

    private static class MemberInfoComparator implements Comparator, Serializable {

        @Override
        public int compare(MemberInfo memberInfo, MemberInfo memberInfo2) {
            int x = memberInfo.name.compareTo(memberInfo2.name);
            if (x != 0) {
                return x;
            }
            return memberInfo.desc.compareTo(memberInfo2.desc);
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy