AI 146870: am: CL 146865 Final pass on stubber: properly filter fields and methods.

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

Automated import of CL 146870
This commit is contained in:
Raphael Moll
2009-04-19 09:47:11 -07:00
committed by The Android Open Source Project
parent 00e25d419c
commit 32557b47c5
7 changed files with 173 additions and 122 deletions

View File

@@ -19,9 +19,7 @@ package com.android.mkstubs;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -68,66 +66,14 @@ class AsmAnalyzer {
} }
} }
public void filter( public void filter(Map<String, ClassReader> classes, Filter filter) {
Map<String, ClassReader> classes,
ArrayList<String> inclusions,
ArrayList<String> exclusions) {
ArrayList<String> inPrefix = new ArrayList<String>();
HashSet <String> inFull = new HashSet <String>();
ArrayList<String> exPrefix = new ArrayList<String>();
HashSet <String> exFull = new HashSet <String>();
for (String in : inclusions) {
if (in.endsWith("*")) {
inPrefix.add(in.substring(0, in.length() - 1));
} else {
inFull.add(in);
}
}
for (String ex : exclusions) {
if (ex.endsWith("*")) {
exPrefix.add(ex.substring(0, ex.length() - 1));
} else {
exFull.add(ex);
}
}
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();
// Check if it can be included.
boolean keep = inFull.contains(key);
if (!keep) {
// Check for a prefix inclusion
for (String prefix : inPrefix) {
if (key.startsWith(prefix)) {
keep = true;
break;
}
}
}
if (keep) {
// check for a full exclusion
keep = !exFull.contains(key);
}
if (keep) {
// or check for prefix exclusion
for (String prefix : exPrefix) {
if (key.startsWith(prefix)) {
keep = false;
break;
}
}
}
// remove if we don't keep it // remove if we don't keep it
if (!keep) { if (!filter.accept(key)) {
System.out.println("- Remove class " + key); System.out.println("- Remove class " + key);
it.remove(); it.remove();
} }

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2009 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.android.mkstubs;
import java.util.TreeSet;
/**
*
*/
class Filter {
private TreeSet<String> mIncludePrefix = new TreeSet<String>();
private TreeSet<String> mIncludeFull = new TreeSet<String>();
private TreeSet<String> mExcludePrefix = new TreeSet<String>();
private TreeSet<String> mExcludeFull = new TreeSet<String>();
public TreeSet<String> getIncludeFull() {
return mIncludeFull;
}
public TreeSet<String> getIncludePrefix() {
return mIncludePrefix;
}
public TreeSet<String> getExcludeFull() {
return mExcludeFull;
}
public TreeSet<String> getExcludePrefix() {
return mExcludePrefix;
}
public boolean accept(String s) {
// Check if it can be included.
boolean accept = mIncludeFull.contains(s);
if (!accept) {
// Check for a prefix inclusion
for (String prefix : mIncludePrefix) {
if (s.startsWith(prefix)) {
accept = true;
break;
}
}
}
if (accept) {
// check for a full exclusion
accept = !mExcludeFull.contains(s);
}
if (accept) {
// or check for prefix exclusion
for (String prefix : mExcludePrefix) {
if (s.startsWith(prefix)) {
accept = false;
break;
}
}
}
return accept;
}
}

View File

@@ -24,27 +24,24 @@ import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import java.util.List;
/** /**
* A class visitor that filters out all the referenced exclusions * A class visitor that filters out all the referenced exclusions
*/ */
class FilterClassAdapter extends ClassAdapter { class FilterClassAdapter extends ClassAdapter {
private final List<String> mExclusions; private final Filter mFilter;
private String mClassName;
public FilterClassAdapter(ClassVisitor writer, List<String> exclusions) { public FilterClassAdapter(ClassVisitor writer, Filter filter) {
super(writer); super(writer);
mExclusions = exclusions; mFilter = filter;
} }
@Override @Override
public void visit(int version, int access, String name, String signature, public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) { String superName, String[] interfaces) {
// TODO filter super type mClassName = name;
// TODO filter interfaces
super.visit(version, access, name, signature, superName, interfaces); super.visit(version, access, name, signature, superName, interfaces);
} }
@@ -71,7 +68,15 @@ class FilterClassAdapter extends ClassAdapter {
return null; return null;
} }
// TODO filter on name // filter on field name
String filterName = String.format("%s#%s", mClassName, name);
if (!mFilter.accept(filterName)) {
System.out.println("- Remove field " + filterName);
return null;
}
// TODO we should produce an error if a filtered desc/signature is being used.
return super.visitField(access, name, desc, signature, value); return super.visitField(access, name, desc, signature, value);
} }
@@ -95,9 +100,25 @@ class FilterClassAdapter extends ClassAdapter {
return null; return null;
} }
// TODO filter exceptions: error if filtered exception is being used // filter on method name using the non-generic descriptor
String filterName = String.format("%s#%s%s", mClassName, name, desc);
// TODO filter on name; error if filtered desc or signatures is being used if (!mFilter.accept(filterName)) {
System.out.println("- Remove method " + filterName);
return null;
}
// filter on method name using the generic signature
if (signature != null) {
filterName = String.format("%s#%s%s", mClassName, name, signature);
if (!mFilter.accept(filterName)) {
System.out.println("- Remove method " + filterName);
return null;
}
}
// TODO we should produce an error if a filtered desc/signature/exception is being used.
return super.visitMethod(access, name, desc, signature, exceptions); return super.visitMethod(access, name, desc, signature, exceptions);
} }
@@ -105,7 +126,7 @@ class FilterClassAdapter extends ClassAdapter {
@Override @Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// Filter on desc type // TODO produce an error if a filtered annotation type is being used
return super.visitAnnotation(desc, visible); return super.visitAnnotation(desc, visible);
} }
@@ -122,14 +143,17 @@ class FilterClassAdapter extends ClassAdapter {
return; return;
} }
// TODO filter on name // filter on name
if (!mFilter.accept(name)) {
return;
}
super.visitInnerClass(name, outerName, innerName, access); super.visitInnerClass(name, outerName, innerName, access);
} }
@Override @Override
public void visitOuterClass(String owner, String name, String desc) { public void visitOuterClass(String owner, String name, String desc) {
// TODO Auto-generated method stub // pass
} }
@Override @Override

View File

@@ -22,7 +22,6 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
@@ -34,12 +33,12 @@ public class Main {
static class Params { static class Params {
private String mInputJarPath; private String mInputJarPath;
private String mOutputJarPath; private String mOutputJarPath;
private ArrayList<String> mInclusions = new ArrayList<String>(); private Filter mFilter;
private ArrayList<String> mExclusions = new ArrayList<String>();
public Params(String inputJarPath, String outputJarPath) { public Params(String inputJarPath, String outputJarPath) {
mInputJarPath = inputJarPath; mInputJarPath = inputJarPath;
mOutputJarPath = outputJarPath; mOutputJarPath = outputJarPath;
mFilter = new Filter();
} }
public String getInputJarPath() { public String getInputJarPath() {
@@ -50,12 +49,8 @@ public class Main {
return mOutputJarPath; return mOutputJarPath;
} }
public ArrayList<String> getExclusions() { public Filter getFilter() {
return mExclusions; return mFilter;
}
public ArrayList<String> getInclusions() {
return mInclusions;
} }
} }
@@ -82,19 +77,43 @@ public class Main {
Params p = new Params(args[0], args[1]); Params p = new Params(args[0], args[1]);
for (int i = 2; i < args.length; i++) { for (int i = 2; i < args.length; i++) {
String s = args[i]; addString(p, args[i]);
if (s.startsWith("@")) {
addStringsFromFile(p, s.substring(1));
} else if (s.startsWith("-")) {
p.getExclusions().add(s.substring(1));
} else if (s.startsWith("+")) {
p.getInclusions().add(s.substring(1));
}
} }
return p; return p;
} }
private void addString(Params p, String s) throws IOException {
s = s.trim();
if (s.length() < 2) {
return;
}
char mode = s.charAt(0);
s = s.substring(1).trim();
if (mode == '@') {
addStringsFromFile(p, s);
} else if (mode == '-') {
s = s.replace('.', '/'); // transform FQCN into ASM internal name
if (s.endsWith("*")) {
p.getFilter().getExcludePrefix().add(s.substring(0, s.length() - 1));
} else {
p.getFilter().getExcludeFull().add(s);
}
} else if (mode == '+') {
s = s.replace('.', '/'); // transform FQCN into ASM internal name
if (s.endsWith("*")) {
p.getFilter().getIncludePrefix().add(s.substring(0, s.length() - 1));
} else {
p.getFilter().getIncludeFull().add(s);
}
}
}
private void addStringsFromFile(Params p, String inputFile) private void addStringsFromFile(Params p, String inputFile)
throws IOException { throws IOException {
BufferedReader br = null; BufferedReader br = null;
@@ -102,22 +121,7 @@ public class Main {
br = new BufferedReader(new FileReader(inputFile)); br = new BufferedReader(new FileReader(inputFile));
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
line = line.trim(); addString(p, line);
if (line.length() == 0) {
continue;
}
char mode = line.charAt(0);
line = line.substring(1).trim();
if (line.length() > 0) {
// Keep all class names in ASM path-like format, e.g. android/view/View
line = line.replace('.', '/');
if (mode == '-') {
p.getExclusions().add(line);
} else if (mode == '+') {
p.getInclusions().add(line);
}
}
} }
} finally { } finally {
br.close(); br.close();
@@ -144,17 +148,21 @@ public class Main {
AsmAnalyzer aa = new AsmAnalyzer(); AsmAnalyzer aa = new AsmAnalyzer();
Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath()); Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath());
aa.filter(classes, p.getInclusions(), p.getExclusions()); System.out.println(String.format("Classes loaded: %d", classes.size()));
aa.filter(classes, p.getFilter());
System.out.println(String.format("Classes filtered: %d", classes.size()));
// dump as Java source files, mostly for debugging // dump as Java source files, mostly for debugging
SourceGenerator src_gen = new SourceGenerator(); SourceGenerator src_gen = new SourceGenerator();
File dst_src_dir = new File(p.getOutputJarPath() + "_sources"); File dst_src_dir = new File(p.getOutputJarPath() + "_sources");
dst_src_dir.mkdir(); dst_src_dir.mkdir();
src_gen.generateSource(dst_src_dir, classes, p.getExclusions()); src_gen.generateSource(dst_src_dir, classes, p.getFilter());
// dump the stubbed jar // dump the stubbed jar
StubGenerator stub_gen = new StubGenerator(); StubGenerator stub_gen = new StubGenerator();
File dst_jar = new File(p.getOutputJarPath()); File dst_jar = new File(p.getOutputJarPath());
stub_gen.generateStubbedJar(dst_jar, classes, p.getExclusions()); stub_gen.generateStubbedJar(dst_jar, classes, p.getFilter());
} }
} }

View File

@@ -26,7 +26,6 @@ import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -41,7 +40,7 @@ class SourceGenerator {
*/ */
public void generateSource(File baseDir, public void generateSource(File baseDir,
Map<String, ClassReader> classes, Map<String, ClassReader> classes,
List<String> exclusions) throws IOException { Filter filter) throws IOException {
for (Entry<String, ClassReader> entry : classes.entrySet()) { for (Entry<String, ClassReader> entry : classes.entrySet()) {
ClassReader cr = entry.getValue(); ClassReader cr = entry.getValue();
@@ -51,7 +50,7 @@ class SourceGenerator {
FileWriter fw = null; FileWriter fw = null;
try { try {
fw = createWriter(baseDir, name); fw = createWriter(baseDir, name);
visitClassSource(fw, cr, exclusions); visitClassSource(fw, cr, filter);
} finally { } finally {
fw.close(); fw.close();
} }
@@ -79,12 +78,12 @@ class SourceGenerator {
* Generate a source equivalent to the stubbed version of the class reader, * Generate a source equivalent to the stubbed version of the class reader,
* minus all exclusions * minus all exclusions
*/ */
void visitClassSource(Writer fw, ClassReader cr, List<String> exclusions) { void visitClassSource(Writer fw, ClassReader cr, Filter filter) {
System.out.println("Dump " + cr.getClassName()); System.out.println("Dump " + cr.getClassName());
ClassVisitor javaWriter = new ClassSourcer(new Output(fw)); ClassVisitor javaWriter = new ClassSourcer(new Output(fw));
ClassVisitor filter = new FilterClassAdapter(javaWriter, exclusions); ClassVisitor classFilter = new FilterClassAdapter(javaWriter, filter);
cr.accept(filter, 0 /*flags*/); cr.accept(classFilter, 0 /*flags*/);
} }
} }

View File

@@ -25,7 +25,6 @@ import org.objectweb.asm.ClassWriter;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -43,14 +42,14 @@ class StubGenerator {
*/ */
public void generateStubbedJar(File destJar, public void generateStubbedJar(File destJar,
Map<String, ClassReader> classes, Map<String, ClassReader> classes,
List<String> exclusions) throws IOException { Filter filter) throws IOException {
TreeMap<String, byte[]> all = new TreeMap<String, byte[]>(); TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
for (Entry<String, ClassReader> entry : classes.entrySet()) { for (Entry<String, ClassReader> entry : classes.entrySet()) {
ClassReader cr = entry.getValue(); ClassReader cr = entry.getValue();
byte[] b = visitClassStubber(cr, exclusions); byte[] b = visitClassStubber(cr, filter);
String name = classNameToEntryPath(cr.getClassName()); String name = classNameToEntryPath(cr.getClassName());
all.put(name, b); all.put(name, b);
} }
@@ -88,7 +87,7 @@ class StubGenerator {
jar.close(); jar.close();
} }
byte[] visitClassStubber(ClassReader cr, List<String> exclusions) { byte[] visitClassStubber(ClassReader cr, Filter filter) {
System.out.println("Stub " + cr.getClassName()); System.out.println("Stub " + cr.getClassName());
// Rewrite the new class from scratch, without reusing the constant pool from the // Rewrite the new class from scratch, without reusing the constant pool from the
@@ -96,8 +95,8 @@ class StubGenerator {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor stubWriter = new ClassStubber(cw); ClassVisitor stubWriter = new ClassStubber(cw);
ClassVisitor filter = new FilterClassAdapter(stubWriter, exclusions); ClassVisitor classFilter = new FilterClassAdapter(stubWriter, filter);
cr.accept(filter, 0 /*flags*/); cr.accept(classFilter, 0 /*flags*/);
return cw.toByteArray(); return cw.toByteArray();
} }
} }

View File

@@ -24,7 +24,6 @@ import org.junit.Test;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList;
/** /**
* *
@@ -48,7 +47,7 @@ public class SourceGeneratorTest {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
ClassReader cr = new ClassReader("data/TestBaseClass"); ClassReader cr = new ClassReader("data/TestBaseClass");
mGen.visitClassSource(sw, cr, new ArrayList<String>()); mGen.visitClassSource(sw, cr, new Filter());
String s = sw.toString(); String s = sw.toString();
Assert.assertNotNull(s); Assert.assertNotNull(s);