Java tutorial
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.camnter.patch.utils.classref; import com.android.dx.cf.direct.DirectClassFile; import com.android.dx.rop.cst.Constant; import com.android.dx.rop.cst.ConstantPool; import com.android.dx.rop.cst.CstFieldRef; import com.android.dx.rop.cst.CstMethodRef; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.Prototype; import com.android.dx.rop.type.StdTypeList; import com.android.dx.rop.type.Type; import com.android.dx.rop.type.TypeList; import com.camnter.patch.utils.NuwaProcessor; import com.google.common.collect.Sets; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import org.apache.commons.io.FileUtils; /** * Tool to find direct class references to other classes. * * https://github.com/dodola/RocooFix/blob/master/buildsrc/src/main/groovy/com/dodola/rocoofix/utils/classref/ClassReferenceListBuilder.java */ public class ClassReferenceListBuilder { private static final String CLASS_EXTENSION = ".class"; private Path path; private final Set<String> alreadyAnalyzedClasses = new HashSet<String>(); private final Set<String> classNames = new HashSet<String>(); private JarFile jarOfRoots; private String refcname; private String currentName; private String patchDir; public ClassReferenceListBuilder(String patchDir) { this.patchDir = patchDir; } public void addRoots(String path) throws IOException { this.path = new Path(path); this.jarOfRoots = new JarFile(path); } public boolean shouldScan(String entryName) { // TODO Need modified return !entryName.startsWith("com/dodola/rocoofix/") && !entryName.startsWith("com/lody/legend/") && !entryName.contains("android/support/"); } public void clearCache() { classNames.clear(); } public void run(String refClassName) throws IOException { classNames.add(refClassName); while (true) { Set<String> temp = getUnAnalyzeClasses(); if (temp.isEmpty()) { break; } for (String tempName : temp) { alreadyAnalyzedClasses.add(tempName); refcname = tempName; // System.out.println("scan:---------->" + refcname); for (Enumeration<? extends ZipEntry> entries = jarOfRoots.entries(); entries.hasMoreElements();) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); currentName = name; if (name.endsWith(CLASS_EXTENSION) && shouldScan(name)) { DirectClassFile classFile; try { classFile = path.getClass(name); } catch (FileNotFoundException e) { throw new IOException("Class " + name + " is missing form original class path " + path, e); } // System.out.println("=====classname:" + name); addDependencies(classFile.getConstantPool()); } } } } } public HashSet<String> getUnAnalyzeClasses() { HashSet<String> temp = Sets.newHashSet(); // for (String alreadyAnalyzedClass : alreadyAnalyzedClasses) { // System.out.println("alreadyAnalyzedClass:---------====--->" + alreadyAnalyzedClass); // } for (String className : classNames) { // System.out.println("classNames:---------====--->" + className); if (!alreadyAnalyzedClasses.contains(className)) { temp.add(className); } } return temp; } public Set<String> getClassNames() { return classNames; } private void addDependencies(ConstantPool pool) { for (Constant constant : pool.getEntries()) { if (constant instanceof CstType) { checkDescriptor(((CstType) constant).getClassType()); } else if (constant instanceof CstFieldRef) { checkDescriptor(((CstFieldRef) constant).getType()); } else if (constant instanceof CstMethodRef) { Prototype proto = ((CstMethodRef) constant).getPrototype(); checkDescriptor(proto.getReturnType()); StdTypeList args = proto.getParameterTypes(); for (int i = 0; i < args.size(); i++) { checkDescriptor(args.get(i)); } } } } private void checkDescriptor(Type type) { String descriptor = type.getDescriptor(); if (descriptor.endsWith(";")) { // System.out.println("=====descriptor:" + descriptor); int lastBrace = descriptor.lastIndexOf('['); if (lastBrace < 0) { addClassWithHierachy(descriptor.substring(1, descriptor.length() - 1)); } else { assert descriptor.length() > lastBrace + 3 && descriptor.charAt(lastBrace + 1) == 'L'; addClassWithHierachy(descriptor.substring(lastBrace + 2, descriptor.length() - 1)); } } } private void addClassWithHierachy(String classBinaryName) { if (classNames.contains(classBinaryName) || !refcname.equals(classBinaryName + CLASS_EXTENSION)) { return; } try { DirectClassFile classFile = path.getClass(currentName); classNames.add(currentName); File entryFile = new File(patchDir + "/" + currentName); entryFile.getParentFile().mkdirs(); if (!entryFile.exists()) { entryFile.createNewFile(); // Iterable<ClassPathElement> elements = path.getElements(); // for (ClassPathElement element : elements) { // InputStream in = element.open(currentName); byte[] bytes = NuwaProcessor.referHackWhenInit(classFile.getBytes().makeDataInputStream()); // System.out.println(classFile.getFilePath() + ",size:" + bytes.length); FileUtils.writeByteArrayToFile(entryFile, bytes); // } } // NuwaProcessor.referHackWhenInit(); CstType superClass = classFile.getSuperclass(); if (superClass != null) { addClassWithHierachy(superClass.getClassType().getClassName()); } TypeList interfaceList = classFile.getInterfaces(); int interfaceNumber = interfaceList.size(); for (int i = 0; i < interfaceNumber; i++) { addClassWithHierachy(interfaceList.getType(i).getClassName()); } } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); } } }