AI 147193: am: CL 147190 Javadoc for MkStubs.

Original author: raphael
  Merged from: //branches/cupcake/...

Automated import of CL 147193
This commit is contained in:
Raphael Moll
2009-04-21 13:22:51 -07:00
committed by The Android Open Source Project
parent 01c1ad4af5
commit ee8f65c4f7
15 changed files with 206 additions and 39 deletions

View File

@@ -28,7 +28,10 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
/** /**
* * Analyzes an input Jar to get all the relevant classes according to the given filter.
* <p/>
* This is mostly a helper extracted for convenience. Callers will want to use
* {@link #parseInputJar(String)} followed by {@link #filter(Map, Filter)}.
*/ */
class AsmAnalyzer { class AsmAnalyzer {
@@ -66,12 +69,22 @@ class AsmAnalyzer {
} }
} }
public void filter(Map<String, ClassReader> classes, Filter filter) { /**
* Filters the set of classes. Removes all classes that should not be included in the
* filter or that should be excluded. This modifies the map in-place.
*
* @param classes The in-out map of classes to examine and filter. The map is filtered
* in-place.
* @param filter A filter describing which classes to include and which ones to exclude.
*/
void filter(Map<String, ClassReader> classes, Filter filter) {
Set<String> keys = classes.keySet(); Set<String> keys = classes.keySet();
for(Iterator<String> it = keys.iterator(); it.hasNext(); ) { for(Iterator<String> it = keys.iterator(); it.hasNext(); ) {
String key = it.next(); String key = it.next();
// TODO: We *could* filter out all private classes here: classes.get(key).getAccess().
// remove if we don't keep it // remove if we don't keep it
if (!filter.accept(key)) { if (!filter.accept(key)) {
System.out.println("- Remove class " + key); System.out.println("- Remove class " + key);

View File

@@ -19,7 +19,15 @@ package com.android.mkstubs;
import java.util.TreeSet; import java.util.TreeSet;
/** /**
* * A "filter" holds the various patterns that MkStubs should accept (include)
* or reject (exclude). Patterns can be of two kind:
* <ul>
* <li>Full patterns are simple string matches, similar to a "^pattern$" regex.
* <li>Prefix patterns are partial string matches, similar to a "^pattern.*" regex.
* </ul>
* <p/>
* The {@link #accept(String)} method examines a given string against the known
* pattern to decide if it should be included.
*/ */
class Filter { class Filter {
private TreeSet<String> mIncludePrefix = new TreeSet<String>(); private TreeSet<String> mIncludePrefix = new TreeSet<String>();
@@ -27,22 +35,44 @@ class Filter {
private TreeSet<String> mExcludePrefix = new TreeSet<String>(); private TreeSet<String> mExcludePrefix = new TreeSet<String>();
private TreeSet<String> mExcludeFull = new TreeSet<String>(); private TreeSet<String> mExcludeFull = new TreeSet<String>();
/**
* Returns the set of all full patterns to be included.
*/
public TreeSet<String> getIncludeFull() { public TreeSet<String> getIncludeFull() {
return mIncludeFull; return mIncludeFull;
} }
/**
* Returns the set of all prefix patterns to be included.
*/
public TreeSet<String> getIncludePrefix() { public TreeSet<String> getIncludePrefix() {
return mIncludePrefix; return mIncludePrefix;
} }
/**
* Returns the set of all full patterns to be excluded.
*/
public TreeSet<String> getExcludeFull() { public TreeSet<String> getExcludeFull() {
return mExcludeFull; return mExcludeFull;
} }
/**
* Returns the set of all prefix patterns to be excluded.
*/
public TreeSet<String> getExcludePrefix() { public TreeSet<String> getExcludePrefix() {
return mExcludePrefix; return mExcludePrefix;
} }
/**
* Checks if the given string passes the various include/exclude rules.
* The matching is done as follows:
* <ul>
* <li> The string must match either a full include or a prefix include.
* <li> The string must not match any full exclude nor any prefix exclude.
* </ul>
* @param s The string to accept or reject.
* @return True if the string can be accepted, false if it must be rejected.
*/
public boolean accept(String s) { public boolean accept(String s) {
// Check if it can be included. // Check if it can be included.

View File

@@ -25,7 +25,8 @@ import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
/** /**
* A class visitor that filters out all the referenced exclusions * A class visitor that filters out all members (fields, methods and inner classes) that are
* either private or rejected by the {@link Filter}.
*/ */
class FilterClassAdapter extends ClassAdapter { class FilterClassAdapter extends ClassAdapter {

View File

@@ -26,10 +26,15 @@ import java.util.Map;
/** /**
* * Main entry point of the MkStubs app.
* <p/>
* For workflow details, see {@link #process(Params)}.
*/ */
public class Main { public class Main {
/**
* A struct-like class to hold the various input values (e.g. command-line args)
*/
static class Params { static class Params {
private String mInputJarPath; private String mInputJarPath;
private String mOutputJarPath; private String mOutputJarPath;
@@ -40,22 +45,25 @@ public class Main {
mOutputJarPath = outputJarPath; mOutputJarPath = outputJarPath;
mFilter = new Filter(); mFilter = new Filter();
} }
/** Returns the name of the input jar, where to read classes from. */
public String getInputJarPath() { public String getInputJarPath() {
return mInputJarPath; return mInputJarPath;
} }
/** Returns the name of the output jar, where to write classes to. */
public String getOutputJarPath() { public String getOutputJarPath() {
return mOutputJarPath; return mOutputJarPath;
} }
/** Returns the current instance of the filter, the include/exclude patterns. */
public Filter getFilter() { public Filter getFilter() {
return mFilter; return mFilter;
} }
} }
/** /**
* @param args * Main entry point. Processes arguments then performs the "real" work.
*/ */
public static void main(String[] args) { public static void main(String[] args) {
@@ -68,6 +76,17 @@ public class Main {
} }
} }
/**
* Grabs command-line arguments.
* The expected arguments are:
* <ul>
* <li> The filename of the input Jar.
* <li> The filename of the output Jar.
* <li> One or more include/exclude patterns or files containing these patterns.
* See {@link #addString(Params, String)} for syntax.
* </ul>
* @throws IOException on failure to read a pattern file.
*/
private Params processArgs(String[] args) throws IOException { private Params processArgs(String[] args) throws IOException {
if (args.length < 2) { if (args.length < 2) {
@@ -83,7 +102,26 @@ public class Main {
return p; return p;
} }
/**
* Adds one pattern string to the current filter.
* The syntax must be:
* <ul>
* <li> +full_include or +prefix_include*
* <li> -full_exclude or -prefix_exclude*
* <li> @filename
* </ul>
* The input string is trimmed so any space around the first letter (-/+/@) or
* at the end is removed. Empty strings are ignored.
*
* @param p The params which filters to edit.
* @param s The string to examine.
* @throws IOException
*/
private void addString(Params p, String s) throws IOException { private void addString(Params p, String s) throws IOException {
if (s == null) {
return;
}
s = s.trim(); s = s.trim();
if (s.length() < 2) { if (s.length() < 2) {
@@ -114,11 +152,20 @@ public class Main {
} }
} }
private void addStringsFromFile(Params p, String inputFile) /**
* Adds all the filter strings from the given file.
*
* @param p The params which filter to edit.
* @param osFilePath The OS path to the file containing the patterns.
* @throws IOException
*
* @see #addString(Params, String)
*/
private void addStringsFromFile(Params p, String osFilePath)
throws IOException { throws IOException {
BufferedReader br = null; BufferedReader br = null;
try { try {
br = new BufferedReader(new FileReader(inputFile)); br = new BufferedReader(new FileReader(osFilePath));
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
addString(p, line); addString(p, line);
@@ -128,6 +175,9 @@ public class Main {
} }
} }
/**
* Prints some help to stdout.
*/
private void usage() { private void usage() {
System.out.println("Usage: mkstub input.jar output.jar [excluded-class @excluded-classes-file ...]"); System.out.println("Usage: mkstub input.jar output.jar [excluded-class @excluded-classes-file ...]");
@@ -144,6 +194,17 @@ public class Main {
System.exit(1); System.exit(1);
} }
/**
* Performs the main workflow of this app:
* <ul>
* <li> Read the input Jar to get all its classes.
* <li> Filter out all classes that should not be included or that should be excluded.
* <li> Goes thru the classes, filters methods/fields and generate their source
* in a directory called "&lt;outpath_jar_path&gt;_sources"
* <li> Does the same filtering on the classes but this time generates the real stubbed
* output jar.
* </ul>
*/
private void process(Params p) throws IOException { private void process(Params p) throws IOException {
AsmAnalyzer aa = new AsmAnalyzer(); AsmAnalyzer aa = new AsmAnalyzer();
Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath()); Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath());

View File

@@ -30,7 +30,11 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
/** /**
* * Given a set of already filtered classes, this filters out all private members and then
* generates the Java source for the remaining classes.
* <p/>
* This is an helper extracted for convenience. Callers just need to use
* {@link #generateSource(File, Map, Filter)}.
*/ */
class SourceGenerator { class SourceGenerator {

View File

@@ -32,7 +32,11 @@ import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
/** /**
* * Given a set of already filtered classes, this filters out all private members,
* stubs the remaining classes and then generates a Jar out of them.
* <p/>
* This is an helper extracted for convenience. Callers just need to use
* {@link #generateStubbedJar(File, Map, Filter)}.
*/ */
class StubGenerator { class StubGenerator {

View File

@@ -19,7 +19,11 @@ package com.android.mkstubs.sourcer;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
/** /**
* * Source generator for the access fields of methods, fields and classes.
* <p/>
* Given an integer access field and a type ({@link #IS_CLASS}, {@link #IS_FIELD} or
* {@link #IS_METHOD}), the {@link #write(int, int)} method can generate a string
* desribing the access modifiers for a Java source.
*/ */
class AccessSourcer { class AccessSourcer {
@@ -81,6 +85,9 @@ class AccessSourcer {
/** /**
* Generates a list of access keywords, e.g. "public final". * Generates a list of access keywords, e.g. "public final".
* <p/>
* It is up to the caller to filter extra keywords that should not be generated,
* e.g. {@link Flag#ACC_SYNTHETIC}.
* *
* @param access The access mode, e.g. 33 or 18 * @param access The access mode, e.g. 33 or 18
* @param filter One of {@link #IS_CLASS}, {@link #IS_FIELD} or {@link #IS_METHOD}, which * @param filter One of {@link #IS_CLASS}, {@link #IS_FIELD} or {@link #IS_METHOD}, which

View File

@@ -19,7 +19,7 @@ package com.android.mkstubs.sourcer;
import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.AnnotationVisitor;
/** /**
* * An annotation visitor that generates Java source for an annotation.
*/ */
class AnnotationSourcer implements AnnotationVisitor { class AnnotationSourcer implements AnnotationVisitor {

View File

@@ -25,7 +25,7 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureReader;
/** /**
* A class visitor that rewrites a java source * A class visitor that writes a java source.
*/ */
public class ClassSourcer implements ClassVisitor { public class ClassSourcer implements ClassVisitor {

View File

@@ -19,10 +19,11 @@ package com.android.mkstubs.sourcer;
import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute; import org.objectweb.asm.Attribute;
import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureReader;
/** /**
* * A field visitor that generates Java source defining a field.
*/ */
class FieldSourcer implements FieldVisitor { class FieldSourcer implements FieldVisitor {
@@ -56,7 +57,7 @@ class FieldSourcer implements FieldVisitor {
as.write(mAccess, AccessSourcer.IS_FIELD); as.write(mAccess, AccessSourcer.IS_FIELD);
if (mSignature == null) { if (mSignature == null) {
mOutput.write(" %s", mOutput.decodeDesc(mDesc)); mOutput.write(" %s", Type.getType(mDesc).getClassName());
} else { } else {
mOutput.write(" "); mOutput.write(" ");
SignatureReader sigReader = new SignatureReader(mSignature); SignatureReader sigReader = new SignatureReader(mSignature);

View File

@@ -26,7 +26,7 @@ import org.objectweb.asm.signature.SignatureReader;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* * A method visitor that generates the Java source for a whole method.
*/ */
class MethodSourcer implements MethodVisitor { class MethodSourcer implements MethodVisitor {

View File

@@ -16,22 +16,38 @@
package com.android.mkstubs.sourcer; package com.android.mkstubs.sourcer;
import org.objectweb.asm.Type;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
/** /**
* * An {@link Output} objects is an helper to write to a character stream {@link Writer}.
* <p/>
* It provide some helper methods to the various "sourcer" classes from this package
* to help them write to the underlying stream.
*/ */
public class Output { public class Output {
private final Writer mWriter; private final Writer mWriter;
/**
* Creates a new {@link Output} object that wraps the given {@link Writer}.
* <p/>
* The caller is responsible of opening and closing the {@link Writer}.
*
* @param writer The writer to write to. Could be a file, a string, etc.
*/
public Output(Writer writer) { public Output(Writer writer) {
mWriter = writer; mWriter = writer;
} }
/**
* Writes a formatted string to the writer.
*
* @param format The format string.
* @param args The arguments for the format string.
*
* @see String#format(String, Object...)
*/
public void write(String format, Object... args) { public void write(String format, Object... args) {
try { try {
mWriter.write(String.format(format, args)); mWriter.write(String.format(format, args));
@@ -40,18 +56,21 @@ public class Output {
} }
} }
/**
* Writes a single character to the writer.
*
* @param c The character to write.
*/
public void write(char c) { public void write(char c) {
write(Character.toString(c)); write(Character.toString(c));
} }
/**
* Writes a {@link StringBuilder} to the writer.
*
* @param sb The {@link StringBuilder#toString()} method is used to ge the string to write.
*/
public void write(StringBuilder sb) { public void write(StringBuilder sb) {
write(sb.toString()); write(sb.toString());
} }
public String decodeDesc(String desc) {
return Type.getType(desc).getClassName();
}
} }

View File

@@ -24,14 +24,22 @@ import org.objectweb.asm.signature.SignatureWriter;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* Note: most of the implementation is a duplicate of * A signature visitor that can be used to generate Java source corresponding to
* ASM's SignatureWriter with some slight variations. * various types of signatures.
* <p/> * <p/>
* Note: When processing a method's signature, the signature order is the * Terminology: a "signature" is a type descriptor for generics. There are different types
* reverse of the source order, e.g. it is (parameters)return-type where * of signatures depending on the context where they are used, e.g. method declarations,
* we want to generate "return-type method-name (parameters)". * method parameters, class declarations, etc..
* So in this case the return-type and parameters are not output directly * <p/>
* but are instead accumulated in internal variables. * Note: most of the implementation is a duplicate of ASM's SignatureWriter with some
* slight variations.
* <p/>
* Note: When processing a method's signature, the signature order is the reverse of the source
* order, e.g. the signature is written as "(parameters)return-type" where we want to generate
* "return-type method-name (parameters)". To hanlde this case, the return-type and parameters
* are <em>not</em> output directly but are instead accumulated in internal variables that you can
* get later using {@link #getReturnType()}, {@link #getParameters()}, {@link #getSuperClass()}
* and {@link #formalsToString()}.
*/ */
class SignatureSourcer implements SignatureVisitor { class SignatureSourcer implements SignatureVisitor {
@@ -58,10 +66,22 @@ class SignatureSourcer implements SignatureVisitor {
*/ */
private int mArgumentStack; private int mArgumentStack;
/**
* {@link SignatureSourcer} generated when parsing the return type of <em>this</em>
* signature. Initially null.
*/
private SignatureSourcer mReturnType; private SignatureSourcer mReturnType;
/**
* {@link SignatureSourcer} generated when parsing the super class of <em>this</em>
* signature. Initially null.
*/
private SignatureSourcer mSuperClass; private SignatureSourcer mSuperClass;
/**
* {@link SignatureSourcer}s for each parameters generated when parsing the method parameters
* of <em>this</em> signature. Initially empty but not null.
*/
private ArrayList<SignatureSourcer> mParameters = new ArrayList<SignatureSourcer>(); private ArrayList<SignatureSourcer> mParameters = new ArrayList<SignatureSourcer>();

View File

@@ -24,7 +24,8 @@ import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
/** /**
* * A class visitor that generates stubs for all methods of the visited class.
* Everything else is passed as-is.
*/ */
public class ClassStubber extends ClassAdapter { public class ClassStubber extends ClassAdapter {

View File

@@ -24,7 +24,13 @@ import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
/** /**
* * A method visitor that generates a code stub for the visited method.
* <p/>
* Annotations and parameters are passed as-is.
* All other code is replaced by the following:
* <pre>throw new RuntimeException("stub");</pre>
* Note that constructors rewritten this way will probably fail with the runtime bytecode
* verifier since no call to <code>super</code> is generated.
*/ */
public class MethodStubber extends MethodAdapter { public class MethodStubber extends MethodAdapter {