fix: store classes access flags in class set
This commit is contained in:
@@ -23,8 +23,9 @@ public class ConvertToClsSet {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ConvertToClsSet.class);
|
||||
|
||||
public static void usage() {
|
||||
LOG.info("<output .jcst file> <several input dex or jar files> ");
|
||||
LOG.info("<android API level (number)> <output .jcst file> <several input dex or jar files> ");
|
||||
LOG.info("Arguments to update core.jcst: "
|
||||
+ "<android API level (number)> "
|
||||
+ "<jadx root>/jadx-core/src/main/resources/clst/core.jcst "
|
||||
+ "<sdk_root>/platforms/android-<api level>/android.jar"
|
||||
+ "<sdk_root>/platforms/android-<api level>/optional/android.car.jar "
|
||||
@@ -32,11 +33,12 @@ public class ConvertToClsSet {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 2) {
|
||||
if (args.length != 5) {
|
||||
usage();
|
||||
System.exit(1);
|
||||
}
|
||||
List<Path> inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList());
|
||||
int androidApiLevel = Integer.parseInt(args[0]);
|
||||
List<Path> inputPaths = Stream.of(args).skip(1).map(Paths::get).collect(Collectors.toList());
|
||||
Path output = inputPaths.remove(0);
|
||||
|
||||
JadxArgs jadxArgs = new JadxArgs();
|
||||
@@ -57,6 +59,7 @@ public class ConvertToClsSet {
|
||||
decompiler.load();
|
||||
RootNode root = decompiler.getRoot();
|
||||
ClsSet set = new ClsSet(root);
|
||||
set.setAndroidApiLevel(androidApiLevel);
|
||||
set.loadFrom(root);
|
||||
set.save(output);
|
||||
|
||||
|
||||
@@ -44,14 +44,17 @@ public class ClsSet {
|
||||
private static final String CLST_PATH = "/clst/" + CLST_FILENAME;
|
||||
|
||||
private static final String JADX_CLS_SET_HEADER = "jadx-cst";
|
||||
private static final int VERSION = 4;
|
||||
private static final int VERSION = 5;
|
||||
|
||||
private static final String STRING_CHARSET = "US-ASCII";
|
||||
|
||||
private static final ArgType[] EMPTY_ARGTYPE_ARRAY = new ArgType[0];
|
||||
private static final ArgType[] OBJECT_ARGTYPE_ARRAY = new ArgType[] { ArgType.OBJECT };
|
||||
|
||||
private final RootNode root;
|
||||
|
||||
private int androidApiLevel;
|
||||
|
||||
public ClsSet(RootNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
@@ -79,7 +82,8 @@ public class ClsSet {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
long time = System.currentTimeMillis() - startTime;
|
||||
int methodsCount = Stream.of(classes).mapToInt(clspClass -> clspClass.getMethodsMap().size()).sum();
|
||||
LOG.debug("Clst file loaded in {}ms, classes: {}, methods: {}", time, classes.length, methodsCount);
|
||||
LOG.debug("Clst file loaded in {}ms, android api: {}, classes: {}, methods: {}",
|
||||
time, androidApiLevel, classes.length, methodsCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +97,7 @@ public class ClsSet {
|
||||
cls.load();
|
||||
|
||||
ClspClassSource source = getClspClassSource(cls);
|
||||
ClspClass nClass = new ClspClass(clsType, k, source);
|
||||
ClspClass nClass = new ClspClass(clsType, k, cls.getAccessFlags().rawValue(), source);
|
||||
if (names.put(clsRawName, nClass) != null) {
|
||||
throw new JadxRuntimeException("Duplicate class: " + clsRawName);
|
||||
}
|
||||
@@ -151,7 +155,11 @@ public class ClsSet {
|
||||
// cls is java.lang.Object
|
||||
return EMPTY_ARGTYPE_ARRAY;
|
||||
}
|
||||
ArgType[] parents = new ArgType[1 + cls.getInterfaces().size()];
|
||||
int interfacesCount = cls.getInterfaces().size();
|
||||
if (interfacesCount == 0 && superClass == ArgType.OBJECT) {
|
||||
return OBJECT_ARGTYPE_ARRAY;
|
||||
}
|
||||
ArgType[] parents = new ArgType[1 + interfacesCount];
|
||||
parents[0] = superClass;
|
||||
int k = 1;
|
||||
for (ArgType iface : cls.getInterfaces()) {
|
||||
@@ -193,10 +201,12 @@ public class ClsSet {
|
||||
DataOutputStream out = new DataOutputStream(output);
|
||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||
out.writeByte(VERSION);
|
||||
out.writeInt(androidApiLevel);
|
||||
|
||||
Map<String, ClspClass> names = new HashMap<>(classes.length);
|
||||
out.writeInt(classes.length);
|
||||
for (ClspClass cls : classes) {
|
||||
out.writeInt(cls.getAccFlags());
|
||||
writeUnsignedByte(out, cls.getSource().ordinal());
|
||||
String clsName = cls.getName();
|
||||
writeString(out, clsName);
|
||||
@@ -243,6 +253,10 @@ public class ClsSet {
|
||||
out.writeByte(-1);
|
||||
return;
|
||||
}
|
||||
if (arr == OBJECT_ARGTYPE_ARRAY) {
|
||||
out.writeByte(-2);
|
||||
return;
|
||||
}
|
||||
int size = arr.length;
|
||||
out.writeByte(size);
|
||||
if (size != 0) {
|
||||
@@ -294,22 +308,22 @@ public class ClsSet {
|
||||
try (DataInputStream in = new DataInputStream(new BufferedInputStream(input))) {
|
||||
byte[] header = new byte[JADX_CLS_SET_HEADER.length()];
|
||||
int readHeaderLength = in.read(header);
|
||||
int version = in.readByte();
|
||||
if (readHeaderLength != JADX_CLS_SET_HEADER.length()
|
||||
|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))
|
||||
|| version != VERSION) {
|
||||
|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))) {
|
||||
throw new DecodeException("Wrong jadx class set header");
|
||||
}
|
||||
int version = in.readByte();
|
||||
if (version != VERSION) {
|
||||
throw new DecodeException("Wrong jadx class set version, got: " + version + ", expect: " + VERSION);
|
||||
}
|
||||
androidApiLevel = in.readInt();
|
||||
int clsCount = in.readInt();
|
||||
classes = new ClspClass[clsCount];
|
||||
ClspClassSource[] clspClassSources = ClspClassSource.values();
|
||||
for (int i = 0; i < clsCount; i++) {
|
||||
int source = readUnsignedByte(in);
|
||||
if (source < 0 || source > clspClassSources.length) {
|
||||
throw new DecodeException("Wrong jadx source identifier");
|
||||
}
|
||||
int accFlags = in.readInt();
|
||||
ClspClassSource clsSource = readClsSource(in);
|
||||
String name = readString(in);
|
||||
classes[i] = new ClspClass(ArgType.object(name), i, clspClassSources[source]);
|
||||
classes[i] = new ClspClass(ArgType.object(name), i, accFlags, clsSource);
|
||||
}
|
||||
for (int i = 0; i < clsCount; i++) {
|
||||
ClspClass nClass = classes[i];
|
||||
@@ -321,6 +335,15 @@ public class ClsSet {
|
||||
}
|
||||
}
|
||||
|
||||
private static ClspClassSource readClsSource(DataInputStream in) throws IOException, DecodeException {
|
||||
int source = readUnsignedByte(in);
|
||||
ClspClassSource[] clspClassSources = ClspClassSource.values();
|
||||
if (source < 0 || source > clspClassSources.length) {
|
||||
throw new DecodeException("Wrong jadx source identifier: " + source);
|
||||
}
|
||||
return clspClassSources[source];
|
||||
}
|
||||
|
||||
private List<ClspMethod> readClsMethods(DataInputStream in, ClassInfo clsInfo) throws IOException {
|
||||
int mCount = in.readShort();
|
||||
List<ClspMethod> methods = new ArrayList<>(mCount);
|
||||
@@ -366,17 +389,20 @@ public class ClsSet {
|
||||
@Nullable
|
||||
private ArgType[] readArgTypesArray(DataInputStream in) throws IOException {
|
||||
int count = in.readByte();
|
||||
if (count == -1) {
|
||||
return null;
|
||||
switch (count) {
|
||||
case -1:
|
||||
return null;
|
||||
case -2:
|
||||
return OBJECT_ARGTYPE_ARRAY;
|
||||
case 0:
|
||||
return EMPTY_ARGTYPE_ARRAY;
|
||||
default:
|
||||
ArgType[] arr = new ArgType[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
arr[i] = readArgType(in);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
if (count == 0) {
|
||||
return EMPTY_ARGTYPE_ARRAY;
|
||||
}
|
||||
ArgType[] arr = new ArgType[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
arr[i] = readArgType(in);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private ArgType readArgType(DataInputStream in) throws IOException {
|
||||
@@ -384,9 +410,6 @@ public class ClsSet {
|
||||
if (ordinal == -1) {
|
||||
return null;
|
||||
}
|
||||
if (ordinal >= TypeEnum.values().length) {
|
||||
throw new JadxRuntimeException("Incorrect ordinal for type enum: " + ordinal);
|
||||
}
|
||||
switch (TypeEnum.values()[ordinal]) {
|
||||
case WILDCARD:
|
||||
ArgType.WildcardBound bound = ArgType.WildcardBound.getByNum(in.readByte());
|
||||
@@ -414,7 +437,7 @@ public class ClsSet {
|
||||
return classes[in.readInt()].getClsType();
|
||||
|
||||
case ARRAY:
|
||||
return ArgType.array(readArgType(in));
|
||||
return ArgType.array(Objects.requireNonNull(readArgType(in)));
|
||||
|
||||
case PRIMITIVE:
|
||||
char shortName = (char) in.readByte();
|
||||
@@ -474,4 +497,12 @@ public class ClsSet {
|
||||
nameMap.put(cls.getName(), cls);
|
||||
}
|
||||
}
|
||||
|
||||
public int getAndroidApiLevel() {
|
||||
return androidApiLevel;
|
||||
}
|
||||
|
||||
public void setAndroidApiLevel(int androidApiLevel) {
|
||||
this.androidApiLevel = androidApiLevel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import jadx.api.plugins.input.data.AccessFlags;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
/**
|
||||
@@ -16,21 +17,17 @@ public class ClspClass {
|
||||
|
||||
private final ArgType clsType;
|
||||
private final int id;
|
||||
private final int accFlags;
|
||||
private ArgType[] parents;
|
||||
private Map<String, ClspMethod> methodsMap = Collections.emptyMap();
|
||||
private List<ArgType> typeParameters = Collections.emptyList();
|
||||
|
||||
private ClspClassSource source;
|
||||
|
||||
public ClspClass(ArgType clsType, int id) {
|
||||
this.clsType = clsType;
|
||||
this.id = id;
|
||||
this.source = ClspClassSource.APP;
|
||||
}
|
||||
|
||||
public ClspClass(ArgType clsType, int id, ClspClassSource source) {
|
||||
public ClspClass(ArgType clsType, int id, int accFlags, ClspClassSource source) {
|
||||
this.clsType = clsType;
|
||||
this.id = id;
|
||||
this.accFlags = accFlags;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@@ -46,6 +43,14 @@ public class ClspClass {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getAccFlags() {
|
||||
return accFlags;
|
||||
}
|
||||
|
||||
public boolean isInterface() {
|
||||
return AccessFlags.hasFlag(accFlags, AccessFlags.INTERFACE);
|
||||
}
|
||||
|
||||
public ArgType[] getParents() {
|
||||
return parents;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
@@ -106,7 +107,7 @@ public class ClspGraph {
|
||||
private void addClass(ClassNode cls) {
|
||||
ArgType clsType = cls.getClassInfo().getType();
|
||||
String rawName = clsType.getObject();
|
||||
ClspClass clspClass = new ClspClass(clsType, -1);
|
||||
ClspClass clspClass = new ClspClass(clsType, -1, cls.getAccessFlags().rawValue(), ClspClassSource.APP);
|
||||
clspClass.setParents(ClsSet.makeParentsArray(cls));
|
||||
nameMap.put(rawName, clspClass);
|
||||
}
|
||||
@@ -174,6 +175,8 @@ public class ClspGraph {
|
||||
return result == null ? Collections.emptySet() : result;
|
||||
}
|
||||
|
||||
private static final Set<String> OBJECT_SINGLE_SET = Collections.singleton(Consts.CLASS_OBJECT);
|
||||
|
||||
private void fillSuperTypesCache() {
|
||||
Map<String, Set<String>> map = new HashMap<>(nameMap.size());
|
||||
Set<String> tmpSet = new HashSet<>();
|
||||
@@ -182,10 +185,25 @@ public class ClspGraph {
|
||||
tmpSet.clear();
|
||||
addSuperTypes(cls, tmpSet);
|
||||
Set<String> result;
|
||||
if (tmpSet.isEmpty()) {
|
||||
result = Collections.emptySet();
|
||||
} else {
|
||||
result = new HashSet<>(tmpSet);
|
||||
int size = tmpSet.size();
|
||||
switch (size) {
|
||||
case 0: {
|
||||
result = Collections.emptySet();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
String supCls = tmpSet.iterator().next();
|
||||
if (supCls.equals(Consts.CLASS_OBJECT)) {
|
||||
result = OBJECT_SINGLE_SET;
|
||||
} else {
|
||||
result = Collections.singleton(supCls);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result = new HashSet<>(tmpSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
map.put(cls.getName(), result);
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user