From a4335fbe026cff184eea04b406343296870ccb2e Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Sat, 18 Apr 2009 22:50:00 -0700 Subject: [PATCH] AI 146829: am: CL 146744 am: CL 146720 ADT Jar Stubber: generate stubbed jar. This also reorganizes some source so it's 90% refactoring. There will be more filtering to do in another CL. Original author: raphael Merged from: //branches/cupcake/... Original author: android-build Automated import of CL 146829 --- .../mkstubs/src/com/android/mkstubs/Main.java | 19 +- ...AsmGenerator.java => SourceGenerator.java} | 10 +- .../com/android/mkstubs/StubGenerator.java | 103 ++++++++++ .../{JavaSourcer.java => ClassSourcer.java} | 4 +- .../android/mkstubs/stubber/ClassStubber.java | 86 +++++++++ .../mkstubs/stubber/MethodStubber.java | 179 ++++++++++++++++++ ...atorTest.java => SourceGeneratorTest.java} | 8 +- ...SourcerTest.java => ClassSourcerTest.java} | 8 +- 8 files changed, 390 insertions(+), 27 deletions(-) rename tools/mkstubs/src/com/android/mkstubs/{AsmGenerator.java => SourceGenerator.java} (90%) create mode 100644 tools/mkstubs/src/com/android/mkstubs/StubGenerator.java rename tools/mkstubs/src/com/android/mkstubs/sourcer/{JavaSourcer.java => ClassSourcer.java} (97%) create mode 100644 tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java create mode 100644 tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java rename tools/mkstubs/tests/com/android/mkstubs/{AsmGeneratorTest.java => SourceGeneratorTest.java} (88%) rename tools/mkstubs/tests/com/android/mkstubs/sourcer/{JavaSourcerTest.java => ClassSourcerTest.java} (95%) diff --git a/tools/mkstubs/src/com/android/mkstubs/Main.java b/tools/mkstubs/src/com/android/mkstubs/Main.java index 017b2f15f..5c6e209de 100644 --- a/tools/mkstubs/src/com/android/mkstubs/Main.java +++ b/tools/mkstubs/src/com/android/mkstubs/Main.java @@ -146,20 +146,15 @@ public class Main { aa.filter(classes, p.getInclusions(), p.getExclusions()); - AsmGenerator gen = new AsmGenerator(); - // dump as Java source files, mostly for debugging - File dst_src_dir = new File(p.getOutputJarPath() + File.separator + "sources"); + SourceGenerator src_gen = new SourceGenerator(); + File dst_src_dir = new File(p.getOutputJarPath() + "_sources"); dst_src_dir.mkdir(); - gen.generateSource(dst_src_dir, classes, p.getExclusions()); + src_gen.generateSource(dst_src_dir, classes, p.getExclusions()); + // dump the stubbed jar + StubGenerator stub_gen = new StubGenerator(); + File dst_jar = new File(p.getOutputJarPath()); + stub_gen.generateStubbedJar(dst_jar, classes, p.getExclusions()); } - - /** @deprecated debug only */ - private void displayClasses(Map classes) { - for(String className : classes.keySet()) { - System.out.println("Found " + className); - } - } - } diff --git a/tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java similarity index 90% rename from tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java rename to tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java index 3446b0053..3eb19d613 100644 --- a/tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java +++ b/tools/mkstubs/src/com/android/mkstubs/SourceGenerator.java @@ -16,7 +16,7 @@ package com.android.mkstubs; -import com.android.mkstubs.sourcer.JavaSourcer; +import com.android.mkstubs.sourcer.ClassSourcer; import com.android.mkstubs.sourcer.Output; import org.objectweb.asm.ClassReader; @@ -33,7 +33,7 @@ import java.util.Map.Entry; /** * */ -class AsmGenerator { +class SourceGenerator { /** * Generate source for the stubbed classes, mostly for debug purposes. @@ -51,7 +51,7 @@ class AsmGenerator { FileWriter fw = null; try { fw = createWriter(baseDir, name); - dumpClass(fw, cr, exclusions); + visitClassSource(fw, cr, exclusions); } finally { fw.close(); } @@ -79,10 +79,10 @@ class AsmGenerator { * Generate a source equivalent to the stubbed version of the class reader, * minus all exclusions */ - void dumpClass(Writer fw, ClassReader cr, List exclusions) { + void visitClassSource(Writer fw, ClassReader cr, List exclusions) { System.out.println("Dump " + cr.getClassName()); - ClassVisitor javaWriter = new JavaSourcer(new Output(fw)); + ClassVisitor javaWriter = new ClassSourcer(new Output(fw)); ClassVisitor filter = new FilterClassAdapter(javaWriter, exclusions); cr.accept(filter, 0 /*flags*/); } diff --git a/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java new file mode 100644 index 000000000..79855acb0 --- /dev/null +++ b/tools/mkstubs/src/com/android/mkstubs/StubGenerator.java @@ -0,0 +1,103 @@ +/* + * 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 com.android.mkstubs.stubber.ClassStubber; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.Map.Entry; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +/** + * + */ +class StubGenerator { + + /** + * Generate source for the stubbed classes, mostly for debug purposes. + * @throws IOException + */ + public void generateStubbedJar(File destJar, + Map classes, + List exclusions) throws IOException { + + TreeMap all = new TreeMap(); + + for (Entry entry : classes.entrySet()) { + ClassReader cr = entry.getValue(); + + byte[] b = visitClassStubber(cr, exclusions); + String name = classNameToEntryPath(cr.getClassName()); + all.put(name, b); + } + + createJar(new FileOutputStream(destJar), all); + + System.out.println(String.format("Wrote %s", destJar.getPath())); + } + + /** + * Utility method that converts a fully qualified java name into a JAR entry path + * e.g. for the input "android.view.View" it returns "android/view/View.class" + */ + String classNameToEntryPath(String className) { + return className.replaceAll("\\.", "/").concat(".class"); + } + + /** + * Writes the JAR file. + * + * @param outStream The file output stream were to write the JAR. + * @param all The map of all classes to output. + * @throws IOException if an I/O error has occurred + */ + void createJar(FileOutputStream outStream, Map all) throws IOException { + JarOutputStream jar = new JarOutputStream(outStream); + for (Entry entry : all.entrySet()) { + String name = entry.getKey(); + JarEntry jar_entry = new JarEntry(name); + jar.putNextEntry(jar_entry); + jar.write(entry.getValue()); + jar.closeEntry(); + } + jar.flush(); + jar.close(); + } + + byte[] visitClassStubber(ClassReader cr, List exclusions) { + System.out.println("Stub " + cr.getClassName()); + + // Rewrite the new class from scratch, without reusing the constant pool from the + // original class reader. + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + + ClassVisitor stubWriter = new ClassStubber(cw); + ClassVisitor filter = new FilterClassAdapter(stubWriter, exclusions); + cr.accept(filter, 0 /*flags*/); + return cw.toByteArray(); + } +} diff --git a/tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java similarity index 97% rename from tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java rename to tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java index fb06c680d..189e1a007 100644 --- a/tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java +++ b/tools/mkstubs/src/com/android/mkstubs/sourcer/ClassSourcer.java @@ -27,13 +27,13 @@ import org.objectweb.asm.signature.SignatureReader; /** * A class visitor that rewrites a java source */ -public class JavaSourcer implements ClassVisitor { +public class ClassSourcer implements ClassVisitor { private final Output mOutput; private final AccessSourcer mAccessSourcer; private String mClassName; - public JavaSourcer(Output output) { + public ClassSourcer(Output output) { mOutput = output; mAccessSourcer = new AccessSourcer(mOutput); } diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java new file mode 100644 index 000000000..dea0a5298 --- /dev/null +++ b/tools/mkstubs/src/com/android/mkstubs/stubber/ClassStubber.java @@ -0,0 +1,86 @@ +/* + * 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.stubber; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; + +/** + * + */ +public class ClassStubber extends ClassAdapter { + + public ClassStubber(ClassVisitor cv) { + super(cv); + } + + @Override + public void visit(int version, int access, + String name, + String signature, + String superName, + String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public void visitEnd() { + super.visitEnd(); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return super.visitAnnotation(desc, visible); + } + + @Override + public void visitAttribute(Attribute attr) { + super.visitAttribute(attr); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { + MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); + return new MethodStubber(mw, access, name, desc, signature, exceptions); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + return super.visitField(access, name, desc, signature, value); + } + + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + super.visitInnerClass(name, outerName, innerName, access); + } + + @Override + public void visitOuterClass(String owner, String name, String desc) { + super.visitOuterClass(owner, name, desc); + } + + @Override + public void visitSource(String source, String debug) { + super.visitSource(source, debug); + } +} diff --git a/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java new file mode 100644 index 000000000..3e200cd94 --- /dev/null +++ b/tools/mkstubs/src/com/android/mkstubs/stubber/MethodStubber.java @@ -0,0 +1,179 @@ +/* + * 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.stubber; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodAdapter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * + */ +public class MethodStubber extends MethodAdapter { + + public MethodStubber(MethodVisitor mw, + int access, String name, String desc, String signature, String[] exceptions) { + super(mw); + } + + @Override + public void visitCode() { + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(36, l0); + mv.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException"); + mv.visitInsn(Opcodes.DUP); + mv.visitLdcInsn("stub"); + mv.visitMethodInsn( + Opcodes.INVOKESPECIAL, // opcode + "java/lang/RuntimeException", // owner + "", // name + "(Ljava/lang/String;)V"); // desc + mv.visitInsn(Opcodes.ATHROW); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable( + "this", // name + "Lcom/android/mkstubs/stubber/MethodStubber;", // desc + null, // signature + l0, // label start + l1, // label end + 0); // index + mv.visitMaxs(3, 1); // maxStack, maxLocals + } + + @Override + public void visitEnd() { + super.visitEnd(); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return super.visitAnnotation(desc, visible); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + return super.visitAnnotationDefault(); + } + + @Override + public void visitAttribute(Attribute attr) { + super.visitAttribute(attr); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { + return super.visitParameterAnnotation(parameter, desc, visible); + } + + // -- stuff that gets skipped + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + // skip + } + + @Override + public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) { + // skip + } + + @Override + public void visitIincInsn(int var, int increment) { + // skip + } + + @Override + public void visitInsn(int opcode) { + // skip + } + + @Override + public void visitIntInsn(int opcode, int operand) { + // skip + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + // skip + } + + @Override + public void visitLabel(Label label) { + // skip + } + + @Override + public void visitLdcInsn(Object cst) { + // skip + } + + @Override + public void visitLineNumber(int line, Label start) { + // skip + } + + @Override + public void visitLocalVariable(String name, String desc, String signature, + Label start, Label end, int index) { + // skip + } + + @Override + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + // skip + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + // skip + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + // skip + } + + @Override + public void visitMultiANewArrayInsn(String desc, int dims) { + // skip + } + + @Override + public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { + // skip + } + + @Override + public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + // skip + } + + @Override + public void visitTypeInsn(int opcode, String type) { + // skip + } + + @Override + public void visitVarInsn(int opcode, int var) { + // skip + } +} diff --git a/tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java b/tools/mkstubs/tests/com/android/mkstubs/SourceGeneratorTest.java similarity index 88% rename from tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java rename to tools/mkstubs/tests/com/android/mkstubs/SourceGeneratorTest.java index dd079d5e1..c413d1241 100644 --- a/tools/mkstubs/tests/com/android/mkstubs/AsmGeneratorTest.java +++ b/tools/mkstubs/tests/com/android/mkstubs/SourceGeneratorTest.java @@ -29,13 +29,13 @@ import java.util.ArrayList; /** * */ -public class AsmGeneratorTest { +public class SourceGeneratorTest { - private AsmGenerator mGen; + private SourceGenerator mGen; @Before public void setUp() throws Exception { - mGen = new AsmGenerator(); + mGen = new SourceGenerator(); } @After @@ -48,7 +48,7 @@ public class AsmGeneratorTest { StringWriter sw = new StringWriter(); ClassReader cr = new ClassReader("data/TestBaseClass"); - mGen.dumpClass(sw, cr, new ArrayList()); + mGen.visitClassSource(sw, cr, new ArrayList()); String s = sw.toString(); Assert.assertNotNull(s); diff --git a/tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java b/tools/mkstubs/tests/com/android/mkstubs/sourcer/ClassSourcerTest.java similarity index 95% rename from tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java rename to tools/mkstubs/tests/com/android/mkstubs/sourcer/ClassSourcerTest.java index 250da2a85..87a4ae80a 100644 --- a/tools/mkstubs/tests/com/android/mkstubs/sourcer/JavaSourcerTest.java +++ b/tools/mkstubs/tests/com/android/mkstubs/sourcer/ClassSourcerTest.java @@ -27,7 +27,7 @@ import java.io.StringWriter; /** * */ -public class JavaSourcerTest extends TestHelper { +public class ClassSourcerTest extends TestHelper { /** * @throws java.lang.Exception @@ -48,7 +48,7 @@ public class JavaSourcerTest extends TestHelper { StringWriter sw = new StringWriter(); ClassReader cr = new ClassReader("data/TestBaseClass"); - JavaSourcer jw = new JavaSourcer(new Output(sw)); + ClassSourcer jw = new ClassSourcer(new Output(sw)); cr.accept(jw, 0); assertSourceEquals( @@ -78,7 +78,7 @@ public class JavaSourcerTest extends TestHelper { StringWriter sw = new StringWriter(); ClassReader cr = new ClassReader("data/TestInnerClass"); - JavaSourcer jw = new JavaSourcer(new Output(sw)); + ClassSourcer jw = new ClassSourcer(new Output(sw)); cr.accept(jw, 0); assertSourceEquals( @@ -106,7 +106,7 @@ public class JavaSourcerTest extends TestHelper { StringWriter sw = new StringWriter(); ClassReader cr = new ClassReader("data/TestTemplateClass"); - JavaSourcer jw = new JavaSourcer(new Output(sw)); + ClassSourcer jw = new ClassSourcer(new Output(sw)); cr.accept(jw, 0); assertSourceEquals(