Skip to content

Commit 07d37af

Browse files
author
Mihail Slavchev
authored
Merge pull request #609 from NativeScript/slavchev/metadata-from-dex
generate metadata for *.dex files
2 parents 8cebb05 + 24a840a commit 07d37af

9 files changed

Lines changed: 413 additions & 40 deletions

File tree

android-metadata-generator/src/src/com/telerik/metadata/Builder.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010

1111
import com.telerik.metadata.TreeNode.FieldInfo;
1212
import com.telerik.metadata.TreeNode.MethodInfo;
13+
import com.telerik.metadata.bcl.JarFile;
1314
import com.telerik.metadata.desc.ClassDescriptor;
1415
import com.telerik.metadata.desc.FieldDescriptor;
1516
import com.telerik.metadata.desc.MethodDescriptor;
1617
import com.telerik.metadata.desc.TypeDescriptor;
18+
import com.telerik.metadata.dx.DexFile;
1719

1820
public class Builder {
1921
private static class MethodNameComparator implements Comparator<MethodDescriptor> {
@@ -29,12 +31,17 @@ public static TreeNode build(String[] paths) throws Exception {
2931
for (String path : paths) {
3032
File file = new File(path);
3133
if (file.exists()) {
32-
if (file.isFile() && path.endsWith(".jar")) {
33-
JarFile jar = JarFile.readJar(path);
34-
ClassRepo.cacheJarFile(jar);
34+
if (file.isFile()) {
35+
if (path.endsWith(".jar")) {
36+
JarFile jar = JarFile.readJar(path);
37+
ClassRepo.addToCache(jar);
38+
} else if (path.endsWith(".dex")) {
39+
DexFile dex = DexFile.readDex(path);
40+
ClassRepo.addToCache(dex);
41+
}
3542
} else if (file.isDirectory()) {
3643
ClassDirectory dir = ClassDirectory.readDirectory(path);
37-
ClassRepo.cacheJarFile(dir);
44+
ClassRepo.addToCache(dir);
3845
}
3946
}
4047
}
@@ -56,6 +63,7 @@ public static TreeNode build(String[] paths) throws Exception {
5663
} catch (Throwable e) {
5764
System.out.println("Skip " + className);
5865
System.out.println("\tError: " + e.toString());
66+
//e.printStackTrace();
5967
}
6068
}
6169

android-metadata-generator/src/src/com/telerik/metadata/ClassRepo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ private ClassRepo() {
1111

1212
private static ArrayList<ClassMapProvider> cachedProviders = new ArrayList<ClassMapProvider>();
1313

14-
public static void cacheJarFile(ClassMapProvider classMapProvider) {
14+
public static void addToCache(ClassMapProvider classMapProvider) {
1515
for (String className : classMapProvider.getClassMap().keySet()) {
1616
for (ClassMapProvider cachedProvider : cachedProviders) {
1717
ClassDescriptor clazz = cachedProvider.getClassMap().get(className);

android-metadata-generator/src/src/com/telerik/metadata/JarFile.java renamed to android-metadata-generator/src/src/com/telerik/metadata/bcl/JarFile.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package com.telerik.metadata;
1+
package com.telerik.metadata.bcl;
22

3-
import com.telerik.metadata.bcl.ClassInfo;
3+
import com.telerik.metadata.ClassMapProvider;
44
import com.telerik.metadata.desc.ClassDescriptor;
55

66
import java.io.FileInputStream;
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.telerik.metadata.dx;
2+
3+
import com.android.dex.ClassData;
4+
import com.android.dex.ClassDef;
5+
import com.android.dex.Dex;
6+
import com.android.dex.MethodId;
7+
import com.android.dex.ProtoId;
8+
import com.android.dx.rop.code.AccessFlags;
9+
import com.android.dx.rop.cst.CstType;
10+
import com.android.dx.rop.type.Type;
11+
import com.telerik.metadata.desc.ClassDescriptor;
12+
import com.telerik.metadata.desc.FieldDescriptor;
13+
import com.telerik.metadata.desc.MethodDescriptor;
14+
15+
import java.util.ArrayList;
16+
import java.util.Arrays;
17+
import java.util.Comparator;
18+
import java.util.List;
19+
20+
public class ClassInfo implements ClassDescriptor {
21+
private final DexFile dexFile;
22+
private final ClassDef classDef;
23+
24+
private final Comparator METHOD_NAME_COMPARATOR = new Comparator<MethodDescriptor>() {
25+
@Override
26+
public int compare(MethodDescriptor m1, MethodDescriptor m2) {
27+
return m1.getName().compareTo(m2.getName());
28+
}
29+
};
30+
31+
public ClassInfo(DexFile dexFile, ClassDef classDef) {
32+
this.dexFile = dexFile;
33+
this.classDef = classDef;
34+
}
35+
36+
@Override
37+
public boolean isClass() {
38+
return !isInterface() && !isEnum();
39+
}
40+
41+
@Override
42+
public boolean isInterface() {
43+
return AccessFlags.isInterface(classDef.getAccessFlags());
44+
}
45+
46+
@Override
47+
public boolean isEnum() {
48+
return AccessFlags.isEnum(classDef.getAccessFlags());
49+
}
50+
51+
@Override
52+
public boolean isStatic() {
53+
return AccessFlags.isStatic(classDef.getAccessFlags());
54+
}
55+
56+
@Override
57+
public boolean isPublic() {
58+
return AccessFlags.isPublic(classDef.getAccessFlags());
59+
}
60+
61+
@Override
62+
public boolean isProtected() {
63+
return AccessFlags.isProtected(classDef.getAccessFlags());
64+
}
65+
66+
@Override
67+
public MethodDescriptor[] getMethods() {
68+
MethodDescriptor[] methods;
69+
Dex dex = dexFile.getDex();
70+
int off = classDef.getClassDataOffset();
71+
if (off != 0) {
72+
ClassData data = dex.readClassData(classDef);
73+
ClassData.Method[] allMethods = data.allMethods();
74+
methods = new MethodDescriptor[allMethods.length];
75+
for (int i=0; i<allMethods.length; i++) {
76+
methods[i] = new MethodInfo(dexFile, allMethods[i]);
77+
}
78+
} else {
79+
methods = new MethodDescriptor[0];
80+
}
81+
Arrays.sort(methods, METHOD_NAME_COMPARATOR);
82+
83+
return methods;
84+
}
85+
86+
@Override
87+
public FieldDescriptor[] getFields() {
88+
FieldDescriptor[] fields;
89+
Dex dex = dexFile.getDex();
90+
int off = classDef.getClassDataOffset();
91+
if (off != 0) {
92+
ClassData data = dex.readClassData(classDef);
93+
ClassData.Field[] allFields = data.allFields();
94+
fields = new FieldDescriptor[allFields.length];
95+
for (int i=0; i<fields.length; i++) {
96+
fields[i] = new FieldInfo(dexFile, allFields[i]);
97+
}
98+
} else {
99+
fields = new FieldDescriptor[0];
100+
}
101+
102+
return fields;
103+
}
104+
105+
@Override
106+
public String[] getInterfaceNames() {
107+
ArrayList<String> names = new ArrayList<String>();
108+
short[] ifaceIndexes = classDef.getInterfaces();
109+
if ((ifaceIndexes != null) && (ifaceIndexes.length > 0)) {
110+
List<String> typeNames = dexFile.getDex().typeNames();
111+
for (short idx: ifaceIndexes) {
112+
String interfaceName = typeNames.get(idx);
113+
names.add(interfaceName);
114+
}
115+
}
116+
return names.toArray(new String[names.size()]);
117+
}
118+
119+
@Override
120+
public String getPackageName() {
121+
String className = getClassName();
122+
String name = className.replace('.', '/');
123+
Type t = Type.internClassName(name);
124+
CstType c = CstType.intern(t);
125+
String packageName = c.getPackageName();
126+
return packageName;
127+
}
128+
129+
@Override
130+
public String getClassName() {
131+
List<String> typeNames = dexFile.getDex().typeNames();
132+
int cdIdx = classDef.getTypeIndex();
133+
String typeName = typeNames.get(cdIdx);
134+
String name = typeName.substring(1, typeName.length()-1).replace('/', '.');
135+
return name;
136+
}
137+
138+
@Override
139+
public String getSuperclassName() {
140+
List<String> typeNames = dexFile.getDex().typeNames();
141+
int idx = classDef.getSupertypeIndex();
142+
String superClassname = typeNames.get(idx);
143+
String name = superClassname.substring(1, superClassname.length()-1).replace('/', '.');
144+
return name;
145+
}
146+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.telerik.metadata.dx;
2+
3+
import com.android.dex.ClassDef;
4+
import com.android.dex.Dex;
5+
import com.telerik.metadata.ClassMapProvider;
6+
import com.telerik.metadata.desc.ClassDescriptor;
7+
8+
import java.io.File;
9+
import java.io.IOException;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.function.Consumer;
14+
15+
public class DexFile implements ClassMapProvider {
16+
private final Dex dex;
17+
private final String path;
18+
private final Map<String, ClassDescriptor> classMap;
19+
private static final String CLASS_EXT = ".dex";
20+
21+
private DexFile(Dex dex, String path) {
22+
this.dex = dex;
23+
this.path = path;
24+
this.classMap = new HashMap<String, ClassDescriptor>();
25+
}
26+
27+
@Override
28+
public Map<String, ClassDescriptor> getClassMap() {
29+
return classMap;
30+
}
31+
32+
@Override
33+
public String getPath() {
34+
return path;
35+
}
36+
37+
public Dex getDex() {
38+
return dex;
39+
}
40+
41+
public static DexFile readDex(String path) throws IOException {
42+
Dex dex = new Dex(new File(path));
43+
DexFile dexFile = new DexFile(dex, path);
44+
45+
List<String> typeNames = dex.typeNames();
46+
47+
for (ClassDef classDef : dex.classDefs()) {
48+
int cdIdx = classDef.getTypeIndex();
49+
String typeName = typeNames.get(cdIdx);
50+
String name = typeName.substring(1, typeName.length()-1).replace('/', '.');
51+
dexFile.classMap.put(name, new ClassInfo(dexFile, classDef));
52+
}
53+
54+
return dexFile;
55+
}
56+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.telerik.metadata.dx;
2+
3+
import com.android.dex.ClassData;
4+
import com.android.dex.Dex;
5+
import com.android.dex.FieldId;
6+
import com.android.dx.rop.code.AccessFlags;
7+
import com.telerik.metadata.desc.FieldDescriptor;
8+
import com.telerik.metadata.desc.TypeDescriptor;
9+
10+
import java.util.List;
11+
12+
public class FieldInfo implements FieldDescriptor {
13+
private final DexFile dexFile;
14+
private final ClassData.Field field;
15+
16+
public FieldInfo(DexFile dexFile, ClassData.Field field) {
17+
this.dexFile = dexFile;
18+
this.field = field;
19+
}
20+
21+
@Override
22+
public boolean isPublic() {
23+
return AccessFlags.isPublic(field.getAccessFlags());
24+
}
25+
26+
@Override
27+
public boolean isProtected() {
28+
return AccessFlags.isProtected(field.getAccessFlags());
29+
}
30+
31+
@Override
32+
public boolean isFinal() {
33+
boolean isFinal = (field.getAccessFlags() & AccessFlags.ACC_FINAL) == AccessFlags.ACC_FINAL;
34+
return isFinal;
35+
}
36+
37+
@Override
38+
public boolean isStatic() {
39+
return AccessFlags.isStatic(field.getAccessFlags());
40+
}
41+
42+
@Override
43+
public String getName() {
44+
Dex dex = dexFile.getDex();
45+
List<String> strings = dex.strings();
46+
int idx = dex.nameIndexFromFieldIndex(field.getFieldIndex());
47+
String fieldName = strings.get(idx);
48+
return fieldName;
49+
}
50+
51+
@Override
52+
public TypeDescriptor getType() {
53+
Dex dex = dexFile.getDex();
54+
List<String> typeNames = dex.typeNames();
55+
FieldId fieldId = dex.fieldIds().get(field.getFieldIndex());
56+
int typeIdx = fieldId.getTypeIndex();
57+
String retTypeName = typeNames.get(typeIdx);
58+
return new TypeInfo(retTypeName);
59+
}
60+
}

0 commit comments

Comments
 (0)