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

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

There is a newer version: 1.0.38
Show newest version
package com.googlecode.d2j.tools.jar;

import com.googlecode.dex2jar.tools.BaseCmd;
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.*;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

import static com.googlecode.d2j.util.AccUtils.*;
import static org.objectweb.asm.Opcodes.*;

public class InitOut {
    private static 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 Set clzMap = new TreeSet();
    private Set clzSet = new TreeSet();
    private Path from;
    private int maxLength = 40;
    private Set memberMap = new TreeSet();
    private int minLength = 2;
    private int pkgIndex = 0;
    private Set pkgMap = new TreeSet();
    private 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.toString());
                } else {
                    memberMap.add("m " + owner + "." + member.name
                            + member.desc.substring(0, member.desc.indexOf(')') + 1) + "=" + sb.toString());
                }
            } 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.toString());
                } else {
                    memberMap.add("m " + owner + "." + member.name + "=" + sb.toString());
                }
            }
        }
    }

    private List collect(Path file) throws IOException {
        final List clzList = new ArrayList();
        final ClassVisitor collectVisitor = new ClassVisitor(ASM4) {
            private static final String ASSERTION_DISABLED_FIELD_NAME = "$assertionsDisabled";
            private static final String ENUM_VALUES_FIELD_NAME = "ENUM$VALUES";
            ClassInfo clz;
            boolean isEnum = false;
            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(ASM4, 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 if (status == 3) {  //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("/"), new BaseCmd.FileVisitorX() {
              @Override
              public void visitFile(Path file, String relative) throws IOException {
                  if (relative.endsWith(".class")) {
                      byte data[] = Files.readAllBytes(file);
                      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);
            Collections.sort(ci.fields, comparator);
            transformerMember(ci.name, ci.fields);
            Collections.sort(ci.methods, comparator);
            transformerMember(ci.name, ci.methods);
        }

    }

    static private class ClassInfo {

        final public String name;
        public List fields = new ArrayList(5);
        public List methods = new ArrayList(5);
        public String suggestName;

        public 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;

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

    private static class MemberInfoComparator implements Comparator {
        @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 - 2024 Weber Informatics LLC | Privacy Policy