com.googlecode.d2j.tools.jar.InitOut Maven / Gradle / Ivy
The 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