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<String, ClassReader> classes) {
- for(String className : classes.keySet()) {
- System.out.println("Found " + className);
- }
- }
-
}
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;
/**
*
*/
-class AsmGenerator {
+class SourceGenerator {
/**
* Generate source for the stubbed classes, mostly for debug purposes.
FileWriter fw = null;
try {
fw = createWriter(baseDir, name);
- dumpClass(fw, cr, exclusions);
+ visitClassSource(fw, cr, exclusions);
} finally {
fw.close();
}
* Generate a source equivalent to the stubbed version of the class reader,
* minus all exclusions
*/
- void dumpClass(Writer fw, ClassReader cr, List<String> exclusions) {
+ void visitClassSource(Writer fw, ClassReader cr, List<String> 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*/);
}
--- /dev/null
+/*
+ * 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<String, ClassReader> classes,
+ List<String> exclusions) throws IOException {
+
+ TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
+
+ for (Entry<String, ClassReader> 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<String,byte[]> all) throws IOException {
+ JarOutputStream jar = new JarOutputStream(outStream);
+ for (Entry<String, byte[]> 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<String> 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();
+ }
+}
/**
* 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);
}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
+ "<init>", // 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
+ }
+}
/**
*
*/
-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
StringWriter sw = new StringWriter();
ClassReader cr = new ClassReader("data/TestBaseClass");
- mGen.dumpClass(sw, cr, new ArrayList<String>());
+ mGen.visitClassSource(sw, cr, new ArrayList<String>());
String s = sw.toString();
Assert.assertNotNull(s);
/**
*
*/
-public class JavaSourcerTest extends TestHelper {
+public class ClassSourcerTest extends TestHelper {
/**
* @throws java.lang.Exception
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(
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(
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(