
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