Skip to content

Instantly share code, notes, and snippets.

@5z1punch
Created June 17, 2021 06:55
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save 5z1punch/6bb00644ce6bea327f42cf72bc620b80 to your computer and use it in GitHub Desktop.
Save 5z1punch/6bb00644ce6bea327f42cf72bc620b80 to your computer and use it in GitHub Desktop.
Autotype Graph
// TypeUtils.java
import javafx.util.Pair;
import java.io.*;
import java.lang.reflect.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TypeUtils {
public static Type getFieldType(Method method, Field field){
Type fieldType;
if (method != null) {
Class<?>[] types;
if ((types = method.getParameterTypes()).length == 1) {
fieldType = method.getGenericParameterTypes()[0];
} else if (types.length == 2 && types[0] == String.class && types[1] == Object.class) {
fieldType = types[0];
} else {
fieldType = method.getGenericReturnType();
}
} else {
fieldType = field.getGenericType();
}
// TODO 暂时不处理范型
// if (clazz != null && fieldClass == Object.class && fieldType instanceof TypeVariable) {
// TypeVariable<?> tv = (TypeVariable<?>) fieldType;
// Type genericFieldType = getInheritGenericType(clazz, type, tv);
// if (genericFieldType != null) {
// this.fieldClass = TypeUtils.getClass(genericFieldType);
// this.fieldType = genericFieldType;
//
// isEnum = fieldClass.isEnum();
// return;
// }
// }
return fieldType;
}
public static Field getField(Class<?> clazz, String fieldName, Field[] declaredFields){
for(Field field : declaredFields){
String itemName = field.getName();
if(fieldName.equals(itemName)){
return field;
}
char c0, c1;
if (fieldName.length() > 2
&& (c0 = fieldName.charAt(0)) >= 'a' && c0 <= 'z'
&& (c1 = fieldName.charAt(1)) >= 'A' && c1 <= 'Z'
&& fieldName.equalsIgnoreCase(itemName)) {
return field;
}
}
Class<?> superClass = clazz.getSuperclass();
if(superClass != null && superClass != Object.class){
return getField(superClass, fieldName, superClass.getDeclaredFields());
}
return null;
}
public static String decapitalize(String name){
if(name == null || name.length() == 0){
return name;
}
if(name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){
return name;
}
char[] chars = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
private static boolean stringFilter(String str) {
String regex = "[ `~!@#%^&*()+=|{}':;',\\[\\]<>/?~! @#¥%……&*()——+|{}【】‘;:”“’。,、?]|\n|\r|\t";
Pattern pattern = Pattern.compile(regex );
Matcher matcher = pattern.matcher(str);
return matcher.find();
}
public static String getPureName(String name){
name = name.replace("/",".").replace("[","");
if(name.startsWith("L") && name.endsWith(";")){
name = name.substring(1, name.length()-1);
}
return name;
}
}
// BlackList.java
import java.util.Arrays;
public class BlackList {
private static long[] denyHashCodes = new long[]{
// from 1.2.67
0x80D0C70BCC2FEA02L,
0x86FC2BF9BEAF7AEFL,
0x87F52A1B07EA33A6L,
0x8EADD40CB2A94443L,
0x8F75F9FA0DF03F80L,
0x9172A53F157930AFL,
0x92122D710E364FB8L,
0x941866E73BEFF4C9L,
0x94305C26580F73C5L,
0x9437792831DF7D3FL,
0xA123A62F93178B20L,
0xA85882CE1044C450L,
0xAA3DAFFDB10C4937L,
0xAC6262F52C98AA39L,
0xAD937A449831E8A0L,
0xAE50DA1FAD60A096L,
0xAFFF4C95B99A334DL,
0xB40F341C746EC94FL,
0xB7E8ED757F5D13A2L,
0xBCDD9DC12766F0CEL,
0xC00BE1DEBAF2808BL,
0xC2664D0958ECFE4CL,
0xC7599EBFE3E72406L,
0xC8D49E5601E661A9L,
0xC963695082FD728EL,
0xD1EFCDF4B3316D34L,
0xDE23A0809A8B9BD6L,
0xDEFC208F237D4104L,
0xDF2DDFF310CDB375L,
0xE09AE4604842582FL,
0xE1919804D5BF468FL,
0xE2EB3AC7E56C467EL,
0xE603D6A51FAD692BL,
0xE9184BE55B1D962AL,
0xE9F20BAD25F60807L,
0xF3702A4A5490B8E8L,
0xF474E44518F26736L,
0xF7E96E74DFA58DBCL,
0xFC773AE20C827691L,
0xFD5BFC610056D720L,
0xFFA15BF021F1E37CL,
0xFFDD1A80F1ED3405L,
0x10E067CD55C5E5L,
0x761619136CC13EL,
0x3085068CB7201B8L,
0x45B11BC78A3ABA3L,
0x55CFCA0F2281C07L,
0xB6E292FA5955ADEL,
0xEE6511B66FD5EF0L,
0x100150A253996624L,
0x10B2BDCA849D9B3EL,
0x144277B467723158L,
0x14DB2E6FEAD04AF0L,
0x154B6CB22D294CFAL,
0x17924CCA5227622AL,
0x193B2697EAAED41AL,
0x1E0A8C3358FF3DAEL,
0x24D2F6048FEF4E49L,
0x24EC99D5E7DC5571L,
0x25E962F1C28F71A2L,
0x275D0732B877AF29L,
0x2ADFEFBBFE29D931L,
0x2B3A37467A344CDFL,
0x2D308DBBC851B0D8L,
0x313BB4ABD8D4554CL,
0x327C8ED7C8706905L,
0x332F0B5369A18310L,
0x339A3E0B6BEEBEE9L,
0x33C64B921F523F2FL,
0x34A81EE78429FDF1L,
0x3826F4B2380C8B9BL,
0x398F942E01920CF0L,
0x3B0B51ECBF6DB221L,
0x42D11A560FC9FBA9L,
0x43320DC9D2AE0892L,
0x440E89208F445FB9L,
0x46C808A4B5841F57L,
0x49312BDAFB0077D9L,
0x4A3797B30328202CL,
0x4BA3E254E758D70DL,
0x4BF881E49D37F530L,
0x4DA972745FEB30C1L,
0x4EF08C90FF16C675L,
0x4FD10DDC6D13821FL,
0x527DB6B46CE3BCBCL,
0x5728504A6D454FFCL,
0x599B5C1213A099ACL,
0x5A5BD85C072E5EFEL,
0x5AB0CB3071AB40D1L,
0x5D74D3E5B9370476L,
0x5D92E6DDDE40ED84L,
0x5F215622FB630753L,
0x62DB241274397C34L,
0x63A220E60A17C7B9L,
0x665C53C311193973L,
0x6749835432E0F0D2L,
0x6A47501EBB2AFDB2L,
0x6FCABF6FA54CAFFFL,
0x746BD4A53EC195FBL,
0x74B50BB9260E31FFL,
0x75CC60F5871D0FD3L,
0x767A586A5107FEEFL,
0x7AA7EE3627A19CF3L
};
public static boolean check(String typeName) {
String className = typeName.replace('$', '.');
final long BASIC = 0xcbf29ce484222325L;
final long PRIME = 0x100000001b3L;
long hash = (((((BASIC ^ className.charAt(0))
* PRIME)
^ className.charAt(1))
* PRIME)
^ className.charAt(2))
* PRIME;
for (int i = 3; i < className.length(); ++i) {
hash ^= className.charAt(i);
hash *= PRIME;
if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {
return false;
}
}
return true;
}
}
// ReflectClassEnumerator.java
import com.google.common.reflect.ClassPath;
import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
public class ReflectClassEnumerator {
private final ClassLoader classLoader;
public ReflectClassEnumerator(ClassLoader classLoader) throws IOException {
this.classLoader = classLoader;
}
public List<Class> getAllClasses() throws IOException {
List<Class> result = new ArrayList<>(getRuntimeClasses());
for (ClassPath.ClassInfo classInfo : ClassPath.from(classLoader).getAllClasses()) {
try {
result.add(classInfo.load());
}
catch (Throwable e){
// 包里某些类和当前runtime的java版本不一样
}
}
return result;
}
public List<Class> getRuntimeClasses() throws IOException {
// A hacky way to get the current JRE's rt.jar. Depending on the class loader, rt.jar may be in the
// bootstrap classloader so all the JDK classes will be excluded from classpath scanning with this!
// However, this only works up to Java 8, since after that Java uses some crazy module magic.
URL stringClassUrl = Object.class.getResource("String.class");
URLConnection connection = stringClassUrl.openConnection();
List<Class> result = new ArrayList<>();
if (connection instanceof JarURLConnection) {
URL runtimeUrl = ((JarURLConnection) connection).getJarFileURL();
URLClassLoader classLoader = new URLClassLoader(new URL[]{runtimeUrl});
for (ClassPath.ClassInfo classInfo : ClassPath.from(classLoader).getAllClasses()) {
try {
result.add(classInfo.load());
}
catch (Throwable e){
// 包里某些类和当前runtime的java版本不一样
}
}
return result;
}
else{
throw new IOException("getRuntimeClasses trick got an error, maybe current jdk is not jdk8");
}
}
}
// AutoTypeDiscovery.java
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class AutoTypeDiscovery {
private boolean cacheLoaded = false;
private static final Logger LOGGER = LoggerFactory.getLogger(AutoTypeDiscovery.class);
private final HashSet<String> autotypeClasses = new HashSet<>(Arrays.asList(initAutoTypeList));
private final HashMap<String, List<Class>> cacheAllAssignMap = new HashMap<>();
private final List<String> hasDiscovered = new ArrayList<>();
private final HashMap<String, Pair<Constructor<?>,String[]>> cacheCreatorConstructor = new HashMap<>();
public List<Class> allClasses;
private final HashSet<String> cantDeserialize = new HashSet<>(Arrays.asList(
// 一些因为很蛋疼的原因无法加载的类
"org.apache.http.impl.bootstrap.WorkerPoolExecutor"
));
private static final String[] initAutoTypeList = {
"java.lang.AutoCloseable",
// "java.util.BitSet",
// "org.springframework.cache.support.NullValue",
// "org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken",
// "org.springframework.security.oauth2.common.DefaultOAuth2AccessToken",
// "org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken",
// "org.springframework.remoting.support.RemoteInvocation",
// "org.springframework.remoting.support.RemoteInvocationResult",
// "org.springframework.security.web.savedrequest.DefaultSavedRequest",
// "org.springframework.security.web.savedrequest.SavedCookie",
// "org.springframework.security.web.csrf.DefaultCsrfToken",
// "org.springframework.security.web.authentication.WebAuthenticationDetails",
// "org.springframework.security.core.context.SecurityContextImpl",
// "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
// "org.springframework.security.core.authority.SimpleGrantedAuthority",
// "org.springframework.security.core.userdetails.User",
// Test ⬇️
// "javax.swing.JEditorPane",
};
private static final List<String> jsonParserClasses = new ArrayList<String>(Arrays.asList(
"java.util.Collection",
"java.util.Set",
"java.util.Map",
"java.util.List",
"java.lang.Throwable"
));
private static final List<String> banParentClasses = new ArrayList<String>(Arrays.asList(
"java.lang.ClassLoader",
"javax.sql.RowSet",
"javax.sql.DataSource"
));
private static final List<String> alreadyLoadedClasses = new ArrayList<String>(Arrays.asList(
"java.text.SimpleDateFormat",
"java.sql.Timestamp",
"java.sql.Date",
"java.sql.Time",
"java.util.Date",
"java.util.Calendar",
"javax.xml.datatype.XMLGregorianCalendar",
"java.lang.Object",
"java.lang.String",
"java.lang.StringBuffer",
"java.lang.StringBuilder",
"java.lang.Character",
"java.lang.Byte",
"java.lang.Short",
"java.lang.Integer",
"java.lang.Long",
"java.math.BigInteger",
"java.math.BigDecimal",
"java.lang.Float",
"java.lang.Double",
"java.lang.Boolean",
"java.lang.Class",
"java.util.concurrent.atomic.AtomicBoolean",
"java.util.concurrent.atomic.AtomicInteger",
"java.util.concurrent.atomic.AtomicLong",
"java.util.concurrent.atomic.AtomicReference",
"java.lang.ref.WeakReference",
"java.lang.ref.SoftReference",
"java.util.UUID",
"java.util.TimeZone",
"java.util.Locale",
"java.util.Currency",
"java.net.Inet4Address",
"java.net.Inet6Address",
"java.net.InetSocketAddress",
"java.io.File",
"java.net.URI",
"java.net.URL",
"java.util.regex.Pattern",
"java.nio.charset.Charset",
"com.alibaba.fastjson.JSONPath",
"java.lang.Number",
"java.util.concurrent.atomic.AtomicIntegerArray",
"java.util.concurrent.atomic.AtomicLongArray",
"java.lang.StackTraceElement",
"java.io.Serializable",
"java.lang.Cloneable",
"java.lang.Comparable",
"java.io.Closeable",
"com.alibaba.fastjson.JSONPObject",
"java.awt.Rectangle",
"java.awt.Point",
"java.awt.Font",
"java.awt.Color"
));
static class AutoTypeGraphUtils{
static String output = "cytoscape";
static String dataFile = "data.js";
static String template = "window.element_data =\n";
static List<HashMap<String, String>> nodes = new ArrayList();
static HashSet<String> autotypeClasses = new HashSet<>();
static List<HashMap<String, String>> edges = new ArrayList();
static List<HashMap<String, String>> scanedNodes = new ArrayList();
static List<HashMap<String, String>> realEdges = new ArrayList();
static HashMap<String, List<HashMap>> edgesNodeMap = new HashMap<>();
static int scanedNodesIndex = 0;
static int realEdgesIndex = 0;
static String scanedNodesStr = "";
static String realEdgesStr = "";
static boolean drawFrame = true;
static int frameNo = 1;
public static void addNode(Class clazz){
HashMap<String, String> node = new HashMap<>();
node.put("id", clazz.getName());
String type;
if(clazz.isInterface()){
type = "Interface";
}
else if(clazz.isEnum()){
type = "Enum";
}
else if(clazz.isMemberClass()){
type = "MemClass";
}
else if(Modifier.isAbstract(clazz.getModifiers())){
type = "AbsClass";
}
else{
type = "Class";
}
node.put("type", type);
nodes.add(node);
autotypeClasses.add(clazz.getName());
if(drawFrame){
saveFrame();
}
}
public static void addEdge(String source, String target, String label){
for(HashMap<String, String> edge: edges){
if(source.equals(edge.get("source"))&& target.equals(edge.get("target"))){
return;
}
}
HashMap<String, String> edge = new HashMap();
edge.put("source", source);
edge.put("target", target);
edge.put("label", label);
edges.add(edge);
String[] keys = new String[] {source, target};
for(String key : keys ){
List<HashMap> _edges = edgesNodeMap.getOrDefault(key, null);
if(_edges==null){
_edges = new ArrayList<>();
_edges.add(edge);
edgesNodeMap.put(key, _edges);
}
else{
_edges.add(edge);
}
}
}
public static void initNode(String[] initClasses){
for(String clazz: initClasses){
HashMap<String, String> node = new HashMap<>();
node.put("id", clazz);
node.put("type", "Source");
scanedNodes.add(node);
autotypeClasses.add(clazz);
}
}
public static void save(){
save(output+ File.separator+dataFile);
}
public static void saveFrame(){
String outputFile = output+File.separator+"frame/data."+frameNo+".js";
save(outputFile);
frameNo++;
}
public static void save(String outputFile){
for(HashMap<String, String> _node: nodes){
String nodeId = _node.get("id");
List<HashMap> _edges = edgesNodeMap.get(nodeId);
for(HashMap<String, String> edge: _edges){
boolean sourceExist = edge.get("source").equals(nodeId);
boolean targetExist = edge.get("target").equals(nodeId);
if(realEdges.contains(edge))
continue;
if(!sourceExist && autotypeClasses.contains(edge.get("source")))
sourceExist = true;
if(!targetExist && autotypeClasses.contains(edge.get("target")))
targetExist = true;
if(sourceExist && targetExist) {
realEdges.add(edge);
}
}
}
scanedNodes.addAll(nodes);
nodes = new ArrayList();
String data = template+"\n{\n\tnodes:[\n";
for(;scanedNodesIndex<scanedNodes.size();scanedNodesIndex++){
HashMap<String, String> node = scanedNodes.get(scanedNodesIndex);
scanedNodesStr += String.format("{data:{id:'%s',type:'%s'}},\n",node.get("id"), node.get("type"));
}
data += scanedNodesStr;
data += "],\n\tedges:[\n";
for(;realEdgesIndex<realEdges.size();realEdgesIndex++){
HashMap<String, String> edge = realEdges.get(realEdgesIndex);
realEdgesStr += String.format("{data:{source:'%s',target:'%s',label:'%s'}},\n",edge.get("source"), edge.get("target"), edge.get("label"));
}
data += realEdgesStr;
data += "]\n\t}\n";
try {
BufferedWriter out = new BufferedWriter(new FileWriter(outputFile));
out.write(data);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private final List<Class> cantExpect = new ArrayList<>(Arrays.asList(
Serializable.class,
Cloneable.class,
Closeable.class,
EventListener.class,
Iterable.class,
Collection.class
));
public AutoTypeDiscovery(ReflectClassEnumerator classResourceEnumerator){
this.allClasses = classResourceEnumerator.getAllClasses();
AutoTypeGraphUtils.initNode(initAutoTypeList);
try {
if(this.cacheLoaded){
return;
}
for(String initClass: initAutoTypeList){
for(Class clazz: this.allClasses){
if(!clazz.getName().equals(initClass)){
continue;
}
try{
mergeToAutotypeClasses(discoverFromClass(clazz));
}
catch (NoClassDefFoundError | TypeNotPresentException ncdfe){
LOGGER.error("Autotype init class loaded failed: "+clazz.getName()
+",need dependencies "+ncdfe.getMessage(), ncdfe);
}
break;
}
}
} catch (IOException e) {
LOGGER.error("AutoTypeDiscovery init failed", e);
}
}
public String[] getInitAutoTypeList(){
return initAutoTypeList;
}
public HashSet<String> getAutotypeClasses(){
return autotypeClasses;
}
public void clearInstance(){
this.allClasses = null;
this.cantDeserialize.clear();
this.cacheAllAssignMap.clear();
this.cacheCreatorConstructor.clear();
this.hasDiscovered.clear();
}
private void mergeToAutotypeClasses(HashSet<Class> shortTmp){
for(Class clazz: shortTmp){
mergeToAutotypeClasses(clazz);
}
}
private boolean canBeDeserialized(Class clazz){
// 此处做检查,如果不管怎么加载都不能使用 java bean deser 反序列化的,就会被丢弃
// 其他地方的检查是不能使用父类反序列化的情况
String classname = clazz.getName();
/* TODO
// 这里获取父类是否可以直接使用 isAssignableFrom
// 保证两个 classloader 加载的类是可以通用的
*/
List<Class> allAssign = getAllAssign(clazz);
allAssign.add(clazz);
for(Class pclazz:allAssign){
if(jsonParserClasses.contains(pclazz.getName())){
return false;
}
}
if(alreadyLoadedClasses.contains(classname)){
return false;
}
if(cantDeserialize.contains(classname)){
return false;
}
Constructor[] constructors = clazz.getDeclaredConstructors();
boolean isInterfaceOrAbstract = clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers());
boolean hasDefaultConstructor = hasDefaultConstructor(clazz, constructors);
if(!isInterfaceOrAbstract && !hasDefaultConstructor){
Pair<Constructor<?>,String[]> pair = getCreatorConstructor(clazz);
String[] paramNames = pair.getValue();
Constructor<?> creatorConstructor = pair.getKey();
if(paramNames == null || creatorConstructor.getParameterTypes().length != paramNames.length){
cantDeserialize.add(classname);
return false;
}
}
return true;
}
private boolean mergeToAutotypeClasses(Class clazz){
if(canBeDeserialized(clazz)){
if(this.autotypeClasses.add(clazz.getName()))
AutoTypeGraphUtils.addNode(clazz);
return true;
}
else{
return false;
}
}
public boolean checkReferenceHandle(String name){
name = TypeUtils.getPureName(name);
if(this.autotypeClasses.contains(name)){
return true;
}
return false;
}
// 该方法只能用于搜索超类
private boolean findInAutotypeClasses(List<Class> shortTmp){
for(Class clazz: shortTmp){
if(banParentClasses.contains(clazz.getName())){
return false;
}
}
for(Class clazz: shortTmp){
if(autotypeClasses.contains(clazz.getName())){
return true;
}
}
return false;
}
private Pair<Constructor<?>,String[]> getCreatorConstructor(Class clazz){
String className = clazz.getName();
if(cacheCreatorConstructor.containsKey(className)){
return cacheCreatorConstructor.get(className);
}
if(cantDeserialize.contains(className)){
return new Pair<>(null,null);
}
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor<?> creatorConstructor = null;
String[] paramNames = null;
for (Constructor constructor : constructors) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (className.equals("org.springframework.security.web.authentication.WebAuthenticationDetails")) {
if (parameterTypes.length == 2 && parameterTypes[0] == String.class && parameterTypes[1] == String.class) {
creatorConstructor = constructor;
creatorConstructor.setAccessible(true);
paramNames = ASMUtils.lookupParameterNames(constructor);
break;
}
}
if (className.equals("org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken")) {
if (parameterTypes.length == 3
&& parameterTypes[0] == Object.class
&& parameterTypes[1] == Object.class
&& parameterTypes[2] == Collection.class) {
creatorConstructor = constructor;
creatorConstructor.setAccessible(true);
paramNames = new String[] {"principal", "credentials", "authorities"};
break;
}
}
if (className.equals("org.springframework.security.core.authority.SimpleGrantedAuthority")) {
if (parameterTypes.length == 1
&& parameterTypes[0] == String.class) {
creatorConstructor = constructor;
paramNames = new String[] {"authority"};
break;
}
}
boolean is_public = (constructor.getModifiers() & Modifier.PUBLIC) != 0;
if (!is_public) {
continue;
}
String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);
if (lookupParameterNames == null || lookupParameterNames.length == 0) {
continue;
}
if (creatorConstructor != null
&& paramNames != null && lookupParameterNames.length <= paramNames.length) {
continue;
}
paramNames = lookupParameterNames;
creatorConstructor = constructor;
}
return new Pair<>(creatorConstructor, paramNames);
}
public HashSet<Class> discoverFromClass(Class clazz) throws IOException {
hasDiscovered.add(clazz.getName());
HashSet<Class> shortTmp = new HashSet<>();
Constructor[] constructors = clazz.getDeclaredConstructors();
boolean isInterfaceOrAbstract = clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers());
boolean hasDefaultConstructor = hasDefaultConstructor(clazz, constructors);
Field[] declaredFields = clazz.getDeclaredFields();
List<Field> fieldList = new ArrayList<>();
if(!hasDefaultConstructor && !isInterfaceOrAbstract){
Class<?>[] types = null;
Pair<Constructor<?>,String[]> pair = getCreatorConstructor(clazz);
String[] paramNames = pair.getValue();
Constructor<?> creatorConstructor = pair.getKey();
if (paramNames != null) {
types = creatorConstructor.getParameterTypes();
}
Type[] genericParameterTypes = creatorConstructor.getGenericParameterTypes();
if (paramNames != null
&& types.length == paramNames.length) {
for (int i = 0; i < types.length && i < genericParameterTypes.length; ++i) {
String paramName = paramNames[i];
Type fieldType = genericParameterTypes[i];
Field field = TypeUtils.getField(clazz, paramName, declaredFields);
fieldList.add(field);
HashSet<Class> interClasses = getInternalClass(fieldType);
shortTmp.addAll(interClasses);
for(Class interClass: interClasses){
AutoTypeGraphUtils.addEdge(clazz.getName(), interClass.getName(), "ConstructorArgs");
}
}
if (!clazz.getName().equals("javax.servlet.http.Cookie")) {
return shortTmp;
}
} else {
cantDeserialize.add(clazz.getName());
return shortTmp;
}
}
// 处理 set 方法
Method[] methods = clazz.getMethods();
for (Method method : methods) { //
String methodName = method.getName();
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
// support builder set
Class<?> returnType = method.getReturnType();
if (!(returnType.equals(Void.TYPE) || returnType.equals(method.getDeclaringClass()))) {
continue;
}
if (method.getDeclaringClass() == Object.class) {
continue;
}
Class<?>[] types = method.getParameterTypes();
if (types.length == 0 || types.length > 2) {
continue;
}
if (methodName.length() < 4 || !methodName.startsWith("set")) {
continue;
}
char c3 = methodName.charAt(3);
String propertyName;
Field field = null;
if (Character.isUpperCase(c3) //
|| c3 > 512 // for unicode method name
) {
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
} else if (c3 == '_') {
propertyName = methodName.substring(4);
field = TypeUtils.getField(clazz, propertyName, declaredFields);
if (field == null) {
String temp = propertyName;
propertyName = methodName.substring(3);
field = TypeUtils.getField(clazz, propertyName, declaredFields);
if (field == null) {
propertyName = temp; //减少修改代码带来的影响
}
}
} else if (c3 == 'f') {
propertyName = methodName.substring(3);
} else if (methodName.length() >= 5 && Character.isUpperCase(methodName.charAt(4))) {
propertyName = TypeUtils.decapitalize(methodName.substring(3));
} else {
propertyName = methodName.substring(3);
field = TypeUtils.getField(clazz, propertyName, declaredFields);
if (field == null) {
continue;
}
}
if (field == null) {
field = TypeUtils.getField(clazz, propertyName, declaredFields);
}
if (field == null && types[0] == boolean.class) {
String isFieldName = "is" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
field = TypeUtils.getField(clazz, isFieldName, declaredFields);
}
if(field!=null){
fieldList.add(field);
}
HashSet<Class> interClasses = getInternalClass(TypeUtils.getFieldType(method, field));
shortTmp.addAll(interClasses);
for(Class interClass: interClasses){
AutoTypeGraphUtils.addEdge(clazz.getName(), interClass.getName(), "SetterArgs");
}
}
Field[] fields = clazz.getFields();
List<Class> interFields = computeFields(fieldList, fields);
shortTmp.addAll(interFields);
for(Class interClass: interFields){
AutoTypeGraphUtils.addEdge(clazz.getName(), interClass.getName(), "PublicField");
}
for (Method method :methods) { // getter methods
String methodName = method.getName();
if (methodName.length() < 4) {
continue;
}
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
if (methodName.startsWith("get") && Character.isUpperCase(methodName.charAt(3))) {
if (method.getParameterTypes().length != 0) {
continue;
}
if (Collection.class.isAssignableFrom(method.getReturnType()) //
|| Map.class.isAssignableFrom(method.getReturnType()) //
|| AtomicBoolean.class == method.getReturnType() //
|| AtomicInteger.class == method.getReturnType() //
|| AtomicLong.class == method.getReturnType() //
) {
String propertyName;
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
boolean contains = false;
for (Field item : fieldList) {
if (item.getName().equals(propertyName)) {
contains = true;
break;
}
}
if(!contains){
HashSet<Class> interClasses = getInternalClass(TypeUtils.getFieldType(method,null));
shortTmp.addAll(interClasses);
for(Class interClass: interClasses){
AutoTypeGraphUtils.addEdge(clazz.getName(), interClass.getName(), "GetterArgs");
}
}
}
}
}
return shortTmp;
}
private List<Class> computeFields(List<Field> fieldList, Field[] fields) throws IOException {
List<Class> shortTmp = new ArrayList<>();
for (Field field : fields) { // public static fields
int modifiers = field.getModifiers();
if ((modifiers & Modifier.STATIC) != 0) {
continue;
}
if ((modifiers & Modifier.FINAL) != 0) {
Class<?> fieldType = field.getType();
boolean supportReadOnly = Map.class.isAssignableFrom(fieldType)
|| Collection.class.isAssignableFrom(fieldType)
|| AtomicLong.class.equals(fieldType) //
|| AtomicInteger.class.equals(fieldType) //
|| AtomicBoolean.class.equals(fieldType);
if (!supportReadOnly) {
continue;
}
}
boolean contains = false;
for (Field item : fieldList) {
if (item.getName().equals(field.getName())) {
contains = true;
break;
}
}
if (contains) {
continue;
}
shortTmp.addAll(getInternalClass(field.getGenericType()));
fieldList.add(field);
}
return shortTmp;
}
public HashSet<Class> getInternalClass(Type[] types) throws IOException {
HashSet<Class> results = new HashSet<>();
for(Type type: types){
HashSet<Class> tmp = getInternalClass(type);
results.addAll(tmp);
}
return results;
}
public HashSet<Class> getInternalClass(Type type) throws IOException {
HashSet<Class> results = new HashSet<>();
if(type instanceof ParameterizedType){
ParameterizedType ptype = (ParameterizedType)type;
// 特例
// 为了适配 com.alibaba.fastjson.util.TypeUtils#createCollection 函数的 bug
// 直接实例化了一些接口
// isAssignableFrom 还写反了,我佛了
if(ptype.getRawType() instanceof Class && java.util.concurrent.BlockingQueue.class.isAssignableFrom((Class<?>) ptype.getRawType())){
return results;
}
HashSet<Class> rawClasses = getInternalClass(ptype.getRawType());
results.addAll(rawClasses);
Type[] ataTypes = ptype.getActualTypeArguments();
for(Type ataType : ataTypes){
HashSet<Class> internalType = getInternalClass(ataType);
results.addAll(internalType);
}
}
else if(type instanceof GenericArrayType){
GenericArrayType gaType = (GenericArrayType)type;
results.addAll(getInternalClass(gaType.getGenericComponentType()));
}
else if(type instanceof TypeVariable){
TypeVariable tvType = (TypeVariable) type;
/* TODO
// 暂时不保存范型的代指类型
// 要注意的是,如果一个范型明确了为某个class,只有这个范型在他所被声明的类中发生set的时候,class才允许被反序列化
// 此处为了方便处理,做了很大的简化
*/
for(Type tvBound: tvType.getBounds()){
if(tvBound instanceof Class && !tvBound.equals(Object.class)){
results.addAll(getInternalClass(tvBound));
}
}
}
else if(type instanceof WildcardType){
WildcardType wType = (WildcardType) type;
Type[] ubType = wType.getUpperBounds();
if(!ubType[0].equals(Object.class)){
results.addAll(getInternalClass(ubType));
}
}
else if(type instanceof Class){
Class cType = (Class)type;
if(cType.isPrimitive()){
return results;
}
if(cType.isArray()){
results.addAll(getInternalClass(cType.getComponentType()));
return results;
}
results.add(cType);
}
else{
throw new IOException("type get failed");
}
return results;
}
public static boolean hasDefaultConstructor(Class clazz, Constructor[] constructors){
if (Modifier.isAbstract(clazz.getModifiers())) {
return false;
}
// Constructor<?> defaultConstructor = null;
for (Constructor<?> constructor : constructors) {
if (constructor.getParameterTypes().length == 0) {
// defaultConstructor = constructor;
return true;
}
}
if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
Class<?>[] types;
for (Constructor<?> constructor : constructors) {
if ((types = constructor.getParameterTypes()).length == 1
&& types[0].equals(clazz.getDeclaringClass())) {
// defaultConstructor = constructor;
return true;
}
}
}
return false;
}
public void save() throws IOException {
AutoTypeGraphUtils.save();
}
public List<Class> getAllAssign(Class clazz){
String classname = clazz.getName();
if(cacheAllAssignMap.containsKey(classname)){
return cacheAllAssignMap.get(classname);
}
List<Class> assignableClasses = new ArrayList<>();
if(clazz.isPrimitive()){
return assignableClasses;
}
List<Class> listSuperClass = new ArrayList<>();
if(!clazz.isInterface()){
Class superclass = clazz.getSuperclass();
while (superclass != null && !superclass.getName().equals("java.lang.Object")) {
listSuperClass.add(superclass);
superclass = superclass.getSuperclass();
}
assignableClasses.addAll(listSuperClass);
}
Stack<Class> stack = new Stack<>();
stack.addAll(listSuperClass);
stack.push(clazz);
while(!stack.isEmpty()){
Class interfaces[] = stack.pop().getInterfaces();
for(Class inter: interfaces){
if(!assignableClasses.contains(inter)){
assignableClasses.add(inter);
stack.push(inter);
}
}
}
// 这里加一个派生的黑名单
assignableClasses.removeAll(cantExpect);
cacheAllAssignMap.put(classname, assignableClasses);
return new ArrayList<>(assignableClasses);
}
public void discover() throws Exception {
if(this.cacheLoaded){
return;
}
int autotypeNum = autotypeClasses.size();
while(true){
for(Class clazz: this.allClasses){
if(hasDiscovered.contains(clazz.getName())){
continue;
}
if(!BlackList.check(clazz.getName())){
continue;
}
try {
if(!canBeDeserialized(clazz)){
continue;
}
if(!Modifier.isPublic(clazz.getModifiers()) && !hasDefaultConstructor(clazz, clazz.getDeclaredConstructors())) {
continue;
}
if (clazz.isMemberClass()) {
if(!Modifier.isStatic(clazz.getModifiers()) && !autotypeClasses.contains(clazz.getDeclaringClass().getName()))
continue;
}
if(clazz.isAnonymousClass()){
continue;
}
List<Class> assignableClasses = getAllAssign(clazz);
if(findInAutotypeClasses(assignableClasses)){
for(Class assignableClass: assignableClasses)
AutoTypeGraphUtils.addEdge(assignableClass.getName(), clazz.getName(), "Inherit");
mergeToAutotypeClasses(clazz);
mergeToAutotypeClasses(discoverFromClass(clazz));
}
}catch (NoClassDefFoundError | TypeNotPresentException | IncompatibleClassChangeError |
java.lang.VerifyError | MalformedParameterizedTypeException | SecurityException ncdfe){
//
}
}
if(autotypeNum == autotypeClasses.size()){
break;
}
else {
autotypeNum = autotypeClasses.size();
}
}
}
public static void main(String[] args) throws Exception {
URL[] u = new URL[]{Paths.get("FullNode.jar").toUri().toURL()};
final ClassLoader classLoader = URLClassLoader classLoader = new URLClassLoader(u);
final ReflectClassEnumerator rce = new ReflectClassEnumerator(classLoader);
AutoTypeDiscovery atd = new AutoTypeDiscovery(rce);
atd.discover();
atd.save();
atd.clearInstance();
}
}
@5z1punch
Copy link
Author

Could you provide a package with all jars to successfully run the tool? Thx.

https://github.com/5z1punch/AutoTypeDiscovery

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment