FileDocCategorySizeDatePackage
AsmAnalyzer.javaAPI DocAndroid 1.5 API27675Wed May 06 22:42:02 BST 2009com.android.tools.layoutlib.create

AsmAnalyzer

public class AsmAnalyzer extends Object
Analyzes the input JAR using the ASM java bytecode manipulation library to list the desired classes and their dependencies.

Fields Summary
private final Log
mLog
Output logger.
private final List
mOsSourceJar
The input source JAR to parse.
private final AsmGenerator
mGen
The generator to fill with the class list and dependency list.
private final String[]
mDeriveFrom
Keep all classes that derive from these one (these included).
private final String[]
mIncludeGlobs
Glob patterns of classes to keep, e.g. "com.foo.*"
Constructors Summary
public AsmAnalyzer(Log log, List osJarPath, AsmGenerator gen, String[] deriveFrom, String[] includeGlobs)
Creates a new analyzer.

param
log The log output.
param
osJarPath The input source JARs to parse.
param
gen The generator to fill with the class list and dependency list.
param
deriveFrom Keep all classes that derive from these one (these included).
param
includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*" ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)

        mLog = log;
        mGen = gen;
        mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
        mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
        mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
    
Methods Summary
public voidanalyze()
Starts the analysis using parameters from the constructor. Fills the generator with classes & dependencies found.


        AsmAnalyzer visitor = this;
        
        Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar);
        mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
                mOsSourceJar.size() > 1 ? "s" : "");
        
        Map<String, ClassReader> found = findIncludes(zipClasses);
        Map<String, ClassReader> deps = findDeps(zipClasses, found);
        
        if (mGen != null) {
            mGen.setKeep(found);
            mGen.setDeps(deps);
        }
    
static java.lang.StringclassReaderToClassName(org.objectweb.asm.ClassReader classReader)
Utility that returns the fully qualified binary class name for a ClassReader. E.g. it returns something like android.view.View.

        if (classReader == null) {
            return null;
        } else {
            return classReader.getClassName().replace('/", '.");
        }
    
org.objectweb.asm.ClassReaderfindClass(java.lang.String className, java.util.Map zipClasses, java.util.Map inOutFound)
Uses ASM to find the class reader for the given FQCN class name. If found, insert it in the in_out_found map. Returns the class reader object.

        ClassReader classReader = zipClasses.get(className);
        if (classReader == null) {
            throw new LogAbortException("Class %s not found by ASM in %s",
                    className, mOsSourceJar);
        }

        inOutFound.put(className, classReader);
        return classReader;
    
voidfindClassesDerivingFrom(java.lang.String super_name, java.util.Map zipClasses, java.util.Map inOutFound)
Checks all the classes defined in the JarClassName instance and uses BCEL to determine if they are derived from the given FQCN super class name. Inserts the super class and all the class objects found in the map.

        ClassReader super_clazz = findClass(super_name, zipClasses, inOutFound);

        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
            String className = entry.getKey();
            if (super_name.equals(className)) {
                continue;
            }
            ClassReader classReader = entry.getValue();
            ClassReader parent_cr = classReader;
            while (parent_cr != null) {
                String parent_name = internalToBinaryClassName(parent_cr.getSuperName());
                if (parent_name == null) {
                    // not found
                    break;
                } else if (super_name.equals(parent_name)) {
                    inOutFound.put(className, classReader);
                    break;
                }
                parent_cr = zipClasses.get(parent_name);
            }
        }
    
java.util.MapfindDeps(java.util.Map zipClasses, java.util.Map inOutKeepClasses)
Finds all dependencies for all classes in keepClasses which are also listed in zipClasses. Returns a map of all the dependencies found.


        TreeMap<String, ClassReader> deps = new TreeMap<String, ClassReader>();
        TreeMap<String, ClassReader> new_deps = new TreeMap<String, ClassReader>();
        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
        TreeMap<String, ClassReader> temp = new TreeMap<String, ClassReader>();

        DependencyVisitor visitor = getVisitor(zipClasses,
                inOutKeepClasses, new_keep,
                deps, new_deps);

        for (ClassReader cr : inOutKeepClasses.values()) {
            cr.accept(visitor, 0 /* flags */);
        }
        
        while (new_deps.size() > 0 || new_keep.size() > 0) {
            deps.putAll(new_deps);
            inOutKeepClasses.putAll(new_keep);

            temp.clear();
            temp.putAll(new_deps);
            temp.putAll(new_keep);
            new_deps.clear();
            new_keep.clear();
            mLog.debug("Found %1$d to keep, %2$d dependencies.",
                    inOutKeepClasses.size(), deps.size());

            for (ClassReader cr : temp.values()) {
                cr.accept(visitor, 0 /* flags */);
            }
        }

        mLog.info("Found %1$d classes to keep, %2$d class dependencies.",
                inOutKeepClasses.size(), deps.size());

        return deps;
    
voidfindGlobs(java.lang.String globPattern, java.util.Map zipClasses, java.util.Map inOutFound)
Insert in the inOutFound map all classes found in zipClasses that match the given glob pattern.

The glob pattern is not a regexp. It only accepts the "*" keyword to mean "anything but a period". The "." and "$" characters match themselves. The "**" keyword means everything including ".".

Examples:

  • com.foo.* matches all classes in the package com.foo but NOT sub-packages.
  • com.foo*.*$Event matches all internal Event classes in a com.foo*.* class.

        // transforms the glob pattern in a regexp:
        // - escape "." with "\."
        // - replace "*" by "[^.]*"
        // - escape "$" with "\$"
        // - add end-of-line match $
        globPattern = globPattern.replaceAll("\\$", "\\\\\\$");
        globPattern = globPattern.replaceAll("\\.", "\\\\.");
        // prevent ** from being altered by the next rule, then process the * rule and finally
        // the real ** rule (which is now @)
        globPattern = globPattern.replaceAll("\\*\\*", "@");
        globPattern = globPattern.replaceAll("\\*", "[^.]*");
        globPattern = globPattern.replaceAll("@", ".*");
        globPattern += "$";

        Pattern regexp = Pattern.compile(globPattern);
        
        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
            String class_name = entry.getKey();
            if (regexp.matcher(class_name).matches()) {
                findClass(class_name, zipClasses, inOutFound);
            }
        }
    
java.util.MapfindIncludes(java.util.Map zipClasses)
Process the "includes" arrays.

This updates the in_out_found map.

        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();

        mLog.debug("Find classes to include.");

        for (String s : mIncludeGlobs) {
            findGlobs(s, zipClasses, found);
        }
        for (String s : mDeriveFrom) {
            findClassesDerivingFrom(s, zipClasses, found);
        }
        
        return found;
    
com.android.tools.layoutlib.create.AsmAnalyzer$DependencyVisitorgetVisitor(java.util.Map zipClasses, java.util.Map inKeep, java.util.Map outKeep, java.util.Map inDeps, java.util.Map outDeps)
Instantiates a new DependencyVisitor. Useful for unit tests.

        return new DependencyVisitor(zipClasses, inKeep, outKeep, inDeps, outDeps);
    
static java.lang.StringinternalToBinaryClassName(java.lang.String className)
Utility that returns the fully qualified binary class name from a path-like FQCN. E.g. it returns android.view.View from android/view/View.

        if (className == null) {
            return null;
        } else {
            return className.replace('/", '.");
        }
    
java.util.MapparseZip(java.util.List jarPathList)
Parses a JAR file and returns a list of all classes founds using a map class name => ASM ClassReader. Class names are in the form "android.view.View".

        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();

        for (String jarPath : jarPathList) {
            ZipFile zip = new ZipFile(jarPath);
            Enumeration<? extends ZipEntry> entries = zip.entries();
            ZipEntry entry;
            while (entries.hasMoreElements()) {
                entry = entries.nextElement();
                if (entry.getName().endsWith(".class")) {
                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
                    String className = classReaderToClassName(cr);
                    classes.put(className, cr);
                }
            }
        }
        
        return classes;