AI 145911: ADT #1778786: tool to generate stubbed jar file.
This is only a preliminary CL. More will follow but this is a good start, with the following caveats: What it does: - take an input jar, a list of includes, a list of excludes. - generate actual Java source for the filtered classes. What it doesn't do yet: - some more work on filtering inner elements (methods, etc.) - properly generate inner classes. - hide synthetic fields. - some classes body are missing - directly generate a stubbed bytecode/jar rather than source. I'll likely want to keep the source generator for debugging purposes or if we want to integrate with a build system instead. - classpath will be changed in the final CL to refer to the external ASM lib rather than the project. I need the source for debugging rigth now. - will review comments before submitting. BUG=1778786 Automated import of CL 145911
This commit is contained in:
committed by
The Android Open Source Project
parent
b20c95f355
commit
6c0d467cc2
137
tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
Normal file
137
tools/mkstubs/src/com/android/mkstubs/AsmAnalyzer.java
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 org.objectweb.asm.ClassReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class AsmAnalyzer {
|
||||
|
||||
/**
|
||||
* 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".
|
||||
*/
|
||||
Map<String,ClassReader> parseInputJar(String inputJarPath) throws IOException {
|
||||
TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
|
||||
|
||||
ZipFile zip = new ZipFile(inputJarPath);
|
||||
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 = classReaderToAsmName(cr);
|
||||
classes.put(className, cr);
|
||||
}
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility that returns the fully qualified ASM class name for a ClassReader.
|
||||
* E.g. it returns something like android/view/View.
|
||||
*/
|
||||
static String classReaderToAsmName(ClassReader classReader) {
|
||||
if (classReader == null) {
|
||||
return null;
|
||||
} else {
|
||||
return classReader.getClassName();
|
||||
}
|
||||
}
|
||||
|
||||
public void 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();
|
||||
for(Iterator<String> it = keys.iterator(); it.hasNext(); ) {
|
||||
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
|
||||
if (!keep) {
|
||||
System.out.println("- Remove class " + key);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
90
tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java
Normal file
90
tools/mkstubs/src/com/android/mkstubs/AsmGenerator.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.sourcer.JavaSourcer;
|
||||
import com.android.mkstubs.sourcer.Output;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class AsmGenerator {
|
||||
|
||||
/**
|
||||
* Generate source for the stubbed classes, mostly for debug purposes.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void generateSource(File baseDir,
|
||||
Map<String, ClassReader> classes,
|
||||
List<String> exclusions) throws IOException {
|
||||
|
||||
for (Entry<String, ClassReader> entry : classes.entrySet()) {
|
||||
ClassReader cr = entry.getValue();
|
||||
|
||||
String name = classNameToJavaPath(cr.getClassName());
|
||||
|
||||
FileWriter fw = null;
|
||||
try {
|
||||
fw = createWriter(baseDir, name);
|
||||
dumpClass(fw, cr, exclusions);
|
||||
} finally {
|
||||
fw.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter createWriter(File baseDir, String name) throws IOException {
|
||||
File f = new File(baseDir, name);
|
||||
f.getParentFile().mkdirs();
|
||||
|
||||
System.out.println("Writing " + f.getPath());
|
||||
|
||||
return new FileWriter(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.java"
|
||||
*/
|
||||
String classNameToJavaPath(String className) {
|
||||
return className.replace('.', '/').concat(".java");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a source equivalent to the stubbed version of the class reader,
|
||||
* minus all exclusions
|
||||
*/
|
||||
void dumpClass(Writer fw, ClassReader cr, List<String> exclusions) {
|
||||
System.out.println("Dump " + cr.getClassName());
|
||||
|
||||
ClassVisitor javaWriter = new JavaSourcer(new Output(fw));
|
||||
ClassVisitor filter = new FilterClassAdapter(javaWriter, exclusions);
|
||||
cr.accept(filter, 0 /*flags*/);
|
||||
}
|
||||
|
||||
}
|
||||
139
tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
Normal file
139
tools/mkstubs/src/com/android/mkstubs/FilterClassAdapter.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 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;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class visitor that filters out all the referenced exclusions
|
||||
*/
|
||||
class FilterClassAdapter extends ClassAdapter {
|
||||
|
||||
private final List<String> mExclusions;
|
||||
|
||||
public FilterClassAdapter(ClassVisitor writer, List<String> exclusions) {
|
||||
super(writer);
|
||||
mExclusions = exclusions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature,
|
||||
String superName, String[] interfaces) {
|
||||
|
||||
// TODO filter super type
|
||||
// TODO filter interfaces
|
||||
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
super.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a field.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Examples:
|
||||
* name = mArg
|
||||
* desc = Ljava/Lang/String;
|
||||
* signature = null (not a template) or template type
|
||||
*/
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc,
|
||||
String signature, Object value) {
|
||||
// exclude private fields
|
||||
if ((access & Opcodes.ACC_PRIVATE) != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO filter on name
|
||||
|
||||
return super.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a method.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Examples:
|
||||
* name = <init>
|
||||
* desc = ()V
|
||||
* signature = null (not a template) or template type
|
||||
*/
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc,
|
||||
String signature, String[] exceptions) {
|
||||
|
||||
// exclude private methods
|
||||
if ((access & Opcodes.ACC_PRIVATE) != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO filter exceptions: error if filtered exception is being used
|
||||
|
||||
// TODO filter on name; error if filtered desc or signatures is being used
|
||||
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
|
||||
// Filter on desc type
|
||||
return super.visitAnnotation(desc, visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// pass
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
|
||||
// exclude private methods
|
||||
if ((access & Opcodes.ACC_PRIVATE) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO filter on name
|
||||
|
||||
super.visitInnerClass(name, outerName, innerName, access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
165
tools/mkstubs/src/com/android/mkstubs/Main.java
Normal file
165
tools/mkstubs/src/com/android/mkstubs/Main.java
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 org.objectweb.asm.ClassReader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
static class Params {
|
||||
private String mInputJarPath;
|
||||
private String mOutputJarPath;
|
||||
private ArrayList<String> mInclusions = new ArrayList<String>();
|
||||
private ArrayList<String> mExclusions = new ArrayList<String>();
|
||||
|
||||
public Params(String inputJarPath, String outputJarPath) {
|
||||
mInputJarPath = inputJarPath;
|
||||
mOutputJarPath = outputJarPath;
|
||||
}
|
||||
|
||||
public String getInputJarPath() {
|
||||
return mInputJarPath;
|
||||
}
|
||||
|
||||
public String getOutputJarPath() {
|
||||
return mOutputJarPath;
|
||||
}
|
||||
|
||||
public ArrayList<String> getExclusions() {
|
||||
return mExclusions;
|
||||
}
|
||||
|
||||
public ArrayList<String> getInclusions() {
|
||||
return mInclusions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
Main m = new Main();
|
||||
try {
|
||||
Params p = m.processArgs(args);
|
||||
m.process(p);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Params processArgs(String[] args) throws IOException {
|
||||
|
||||
if (args.length < 2) {
|
||||
usage();
|
||||
}
|
||||
|
||||
Params p = new Params(args[0], args[1]);
|
||||
|
||||
for (int i = 2; i < args.length; i++) {
|
||||
String s = 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;
|
||||
}
|
||||
|
||||
private void addStringsFromFile(Params p, String inputFile)
|
||||
throws IOException {
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
br = new BufferedReader(new FileReader(inputFile));
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
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 {
|
||||
br.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void usage() {
|
||||
System.out.println("Usage: mkstub input.jar output.jar [excluded-class @excluded-classes-file ...]");
|
||||
|
||||
System.out.println("Include syntax:\n" +
|
||||
"+com.package.* : whole package, with glob\n" +
|
||||
"+com.package.Class[$Inner] or ...Class*: whole classes with optional glob\n" +
|
||||
"Inclusion is not supported at method/field level.\n\n");
|
||||
|
||||
System.out.println("Exclude syntax:\n" +
|
||||
"-com.package.* : whole package, with glob\n" +
|
||||
"-com.package.Class[$Inner] or ...Class*: whole classes with optional glob\n" +
|
||||
"-com.package.Class#method: whole method or field\n" +
|
||||
"-com.package.Class#method(IILjava/lang/String;)V: specific method with signature.\n\n");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private void process(Params p) throws IOException {
|
||||
AsmAnalyzer aa = new AsmAnalyzer();
|
||||
Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath());
|
||||
|
||||
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");
|
||||
dst_src_dir.mkdir();
|
||||
gen.generateSource(dst_src_dir, classes, p.getExclusions());
|
||||
|
||||
}
|
||||
|
||||
/** @deprecated debug only */
|
||||
private void displayClasses(Map<String, ClassReader> classes) {
|
||||
for(String className : classes.keySet()) {
|
||||
System.out.println("Found " + className);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
104
tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
Normal file
104
tools/mkstubs/src/com/android/mkstubs/sourcer/AccessSourcer.java
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.sourcer;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class AccessSourcer {
|
||||
|
||||
private final Output mOutput;
|
||||
|
||||
public static int IS_CLASS = 1;
|
||||
public static int IS_FIELD = 2;
|
||||
public static int IS_METHOD = 4;
|
||||
|
||||
private enum Flag {
|
||||
ACC_PUBLIC(Opcodes.ACC_PUBLIC , IS_CLASS | IS_FIELD | IS_METHOD),
|
||||
ACC_PRIVATE(Opcodes.ACC_PRIVATE , IS_CLASS | IS_FIELD | IS_METHOD),
|
||||
ACC_PROTECTED(Opcodes.ACC_PROTECTED , IS_CLASS | IS_FIELD | IS_METHOD),
|
||||
ACC_STATIC(Opcodes.ACC_STATIC , IS_FIELD | IS_METHOD),
|
||||
ACC_FINAL(Opcodes.ACC_FINAL , IS_CLASS | IS_FIELD | IS_METHOD),
|
||||
ACC_SUPER(Opcodes.ACC_SUPER , IS_CLASS),
|
||||
ACC_SYNCHRONIZED(Opcodes.ACC_SYNCHRONIZED , IS_METHOD),
|
||||
ACC_VOLATILE(Opcodes.ACC_VOLATILE , IS_FIELD),
|
||||
ACC_BRIDGE(Opcodes.ACC_BRIDGE , IS_METHOD),
|
||||
ACC_VARARGS(Opcodes.ACC_VARARGS , IS_METHOD),
|
||||
ACC_TRANSIENT(Opcodes.ACC_TRANSIENT , IS_FIELD),
|
||||
ACC_NATIVE(Opcodes.ACC_NATIVE , IS_METHOD),
|
||||
ACC_INTERFACE(Opcodes.ACC_INTERFACE , IS_CLASS),
|
||||
ACC_ABSTRACT(Opcodes.ACC_ABSTRACT , IS_CLASS | IS_METHOD),
|
||||
ACC_STRICT(Opcodes.ACC_STRICT , IS_METHOD),
|
||||
ACC_SYNTHETIC(Opcodes.ACC_SYNTHETIC , IS_CLASS | IS_FIELD | IS_METHOD),
|
||||
ACC_ANNOTATION(Opcodes.ACC_ANNOTATION , IS_CLASS),
|
||||
ACC_ENUM(Opcodes.ACC_ENUM , IS_CLASS),
|
||||
ACC_DEPRECATED(Opcodes.ACC_DEPRECATED , IS_CLASS | IS_FIELD | IS_METHOD)
|
||||
;
|
||||
|
||||
private final int mValue;
|
||||
private final int mFilter;
|
||||
|
||||
private Flag(int value, int filter) {
|
||||
mValue = value;
|
||||
mFilter = filter;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public int getFilter() {
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
/** Transforms "ACC_PUBLIC" into "public" */
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString().substring(4).toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public AccessSourcer(Output output) {
|
||||
mOutput = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of access keywords, e.g. "public final".
|
||||
*
|
||||
* @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
|
||||
* indicates the validity context.
|
||||
*/
|
||||
public void write(int access, int filter) {
|
||||
|
||||
boolean need_sep = false;
|
||||
|
||||
for (Flag f : Flag.values()) {
|
||||
if ((f.getFilter() & filter) != 0 && (access & f.getValue()) != 0) {
|
||||
if (need_sep) {
|
||||
mOutput.write(" ");
|
||||
}
|
||||
mOutput.write(f.toString());
|
||||
need_sep = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.sourcer;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class AnnotationSourcer implements AnnotationVisitor {
|
||||
|
||||
private final String mOpenChar;
|
||||
private final String mCloseChar;
|
||||
private final Output mOutput;
|
||||
private boolean mNeedClose;
|
||||
|
||||
public AnnotationSourcer(Output output) {
|
||||
this(output, false /*isArray*/);
|
||||
}
|
||||
|
||||
public AnnotationSourcer(Output output, boolean isArray) {
|
||||
mOutput = output;
|
||||
mOpenChar = isArray ? "[" : "(";
|
||||
mCloseChar = isArray ? "]" : ")";
|
||||
}
|
||||
|
||||
public void visit(String name, Object value) {
|
||||
startOpen();
|
||||
|
||||
if (name != null) {
|
||||
mOutput.write("%s=", name);
|
||||
}
|
||||
if (value != null) {
|
||||
mOutput.write(name.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void startOpen() {
|
||||
if (!mNeedClose) {
|
||||
mNeedClose = true;
|
||||
mOutput.write(mOpenChar);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
if (mNeedClose) {
|
||||
mOutput.write(mCloseChar);
|
||||
}
|
||||
mOutput.write("\n");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
startOpen();
|
||||
|
||||
mOutput.write("@%s", name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
startOpen();
|
||||
return new AnnotationSourcer(mOutput, true /*isArray*/);
|
||||
}
|
||||
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
mOutput.write("/* annotation enum not supported: %s */\n", name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.sourcer;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.signature.SignatureReader;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class FieldSourcer implements FieldVisitor {
|
||||
|
||||
private final Output mOutput;
|
||||
private final int mAccess;
|
||||
private final String mName;
|
||||
private final String mDesc;
|
||||
private final String mSignature;
|
||||
|
||||
public FieldSourcer(Output output, int access, String name, String desc, String signature) {
|
||||
mOutput = output;
|
||||
mAccess = access;
|
||||
mName = name;
|
||||
mDesc = desc;
|
||||
mSignature = signature;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
mOutput.write("@%s", desc);
|
||||
return new AnnotationSourcer(mOutput);
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
mOutput.write("%s /* non-standard attribute */ ", attr.type);
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
// Need to write type and field name after the annotations and attributes.
|
||||
|
||||
AccessSourcer as = new AccessSourcer(mOutput);
|
||||
as.write(mAccess, AccessSourcer.IS_FIELD);
|
||||
|
||||
if (mSignature == null) {
|
||||
mOutput.write(" %s", mOutput.decodeDesc(mDesc));
|
||||
} else {
|
||||
mOutput.write(" ");
|
||||
SignatureReader sigReader = new SignatureReader(mSignature);
|
||||
SignatureSourcer sigSourcer = new SignatureSourcer();
|
||||
sigReader.acceptType(sigSourcer);
|
||||
mOutput.write(sigSourcer.toString());
|
||||
}
|
||||
|
||||
mOutput.write(" %s", mName);
|
||||
|
||||
mOutput.write(";\n");
|
||||
}
|
||||
|
||||
}
|
||||
139
tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java
Normal file
139
tools/mkstubs/src/com/android/mkstubs/sourcer/JavaSourcer.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.sourcer;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.signature.SignatureReader;
|
||||
|
||||
/**
|
||||
* A class visitor that rewrites a java source
|
||||
*/
|
||||
public class JavaSourcer implements ClassVisitor {
|
||||
|
||||
private final Output mOutput;
|
||||
private final AccessSourcer mAccessSourcer;
|
||||
private String mClassName;
|
||||
|
||||
public JavaSourcer(Output output) {
|
||||
mOutput = output;
|
||||
mAccessSourcer = new AccessSourcer(mOutput);
|
||||
}
|
||||
|
||||
/* Examples:
|
||||
* name = com/foo/MyClass
|
||||
* signature = null (if not generic)
|
||||
* superName = java/lang/Object
|
||||
* interfaces = [ java/lang/Runnable ... ]
|
||||
*/
|
||||
public void visit(int version, int access, String name, String signature,
|
||||
String superName, String[] interfaces) {
|
||||
|
||||
String pkg = name.substring(0, name.lastIndexOf('/')).replace('/', '.');
|
||||
mClassName = name.substring(name.lastIndexOf('/') + 1);
|
||||
|
||||
mOutput.write("package %s;\n", pkg);
|
||||
|
||||
// dump access keywords. Note: do not dump "super" here
|
||||
mAccessSourcer.write(access & ~Opcodes.ACC_SUPER, AccessSourcer.IS_CLASS);
|
||||
|
||||
// write class name
|
||||
mOutput.write(" class %s", mClassName);
|
||||
|
||||
if (signature != null) {
|
||||
// write template formal definition and super type
|
||||
SignatureReader sigReader = new SignatureReader(signature);
|
||||
SignatureSourcer sigSourcer = new SignatureSourcer();
|
||||
sigReader.accept(sigSourcer);
|
||||
|
||||
if (sigSourcer.hasFormalsContent()) {
|
||||
mOutput.write(sigSourcer.formalsToString());
|
||||
}
|
||||
|
||||
mOutput.write(" extends %s", sigSourcer.getSuperClass().toString());
|
||||
|
||||
} else {
|
||||
// write non-generic super type
|
||||
mOutput.write(" extends %s", superName.replace('/', '.'));
|
||||
}
|
||||
|
||||
// write interfaces defined, if any
|
||||
if (interfaces != null && interfaces.length > 0) {
|
||||
mOutput.write(" implements ");
|
||||
boolean need_sep = false;
|
||||
for (String i : interfaces) {
|
||||
if (need_sep) {
|
||||
mOutput.write(", ");
|
||||
}
|
||||
mOutput.write(i.replace('/', '.'));
|
||||
need_sep = true;
|
||||
}
|
||||
}
|
||||
|
||||
// open class body
|
||||
mOutput.write(" {\n");
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
mOutput.write("}\n");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
mOutput.write("@%s", desc);
|
||||
return new AnnotationSourcer(mOutput);
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
mOutput.write("%s /* non-standard class attribute */ ", attr.type);
|
||||
}
|
||||
|
||||
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature,
|
||||
Object value) {
|
||||
// skip synthetic fields
|
||||
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FieldSourcer(mOutput, access, name, desc, signature);
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
|
||||
String[] exceptions) {
|
||||
|
||||
// Visit the method and dump its stub.
|
||||
return new MethodSourcer(mOutput, mClassName, access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
// Skip inner classes. This just indicates there's an inner class definition but
|
||||
// they are visited at the top level as separate classes.
|
||||
}
|
||||
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
// Skip outer classes.
|
||||
}
|
||||
|
||||
public void visitSource(String source, String debug) {
|
||||
// Skip source information.
|
||||
}
|
||||
|
||||
}
|
||||
235
tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
Normal file
235
tools/mkstubs/src/com/android/mkstubs/sourcer/MethodSourcer.java
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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.sourcer;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.signature.SignatureReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class MethodSourcer implements MethodVisitor {
|
||||
|
||||
private final Output mOutput;
|
||||
private final int mAccess;
|
||||
private final String mClassName;
|
||||
private final String mName;
|
||||
private final String mDesc;
|
||||
private final String mSignature;
|
||||
private final String[] mExceptions;
|
||||
private boolean mNeedDeclaration;
|
||||
private boolean mIsConstructor;
|
||||
|
||||
public MethodSourcer(Output output, String className, int access, String name,
|
||||
String desc, String signature, String[] exceptions) {
|
||||
mOutput = output;
|
||||
mClassName = className;
|
||||
mAccess = access;
|
||||
mName = name;
|
||||
mDesc = desc;
|
||||
mSignature = signature;
|
||||
mExceptions = exceptions;
|
||||
|
||||
mNeedDeclaration = true;
|
||||
mIsConstructor = "<init>".equals(name);
|
||||
}
|
||||
|
||||
private void writeHeader() {
|
||||
if (!mNeedDeclaration) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccessSourcer as = new AccessSourcer(mOutput);
|
||||
as.write(mAccess, AccessSourcer.IS_METHOD);
|
||||
|
||||
// preprocess the signature to get the return type and the arguments
|
||||
SignatureSourcer sigSourcer = null;
|
||||
if (mSignature != null) {
|
||||
SignatureReader sigReader = new SignatureReader(mSignature);
|
||||
sigSourcer = new SignatureSourcer();
|
||||
sigReader.accept(sigSourcer);
|
||||
|
||||
if (sigSourcer.hasFormalsContent()) {
|
||||
// dump formal template parameter definitions
|
||||
mOutput.write(" %s", sigSourcer.formalsToString());
|
||||
}
|
||||
}
|
||||
|
||||
// output return type (constructor have no return type)
|
||||
if (!mIsConstructor) {
|
||||
// The signature overrides desc, if present
|
||||
if (sigSourcer == null || sigSourcer.getReturnType() == null) {
|
||||
mOutput.write(" %s", Type.getReturnType(mDesc).getClassName());
|
||||
|
||||
} else {
|
||||
mOutput.write(" %s", sigSourcer.getReturnType().toString());
|
||||
}
|
||||
}
|
||||
|
||||
// output name
|
||||
mOutput.write(" %s(", mIsConstructor ? mClassName : mName);
|
||||
|
||||
// output arguments. The signature overrides desc, if present
|
||||
if (mSignature == null) {
|
||||
Type[] types = Type.getArgumentTypes(mDesc);
|
||||
|
||||
for(int i = 0; i < types.length; i++) {
|
||||
if (i > 0) {
|
||||
mOutput.write(", ");
|
||||
}
|
||||
mOutput.write("%s arg%d", types[i].getClassName(), i);
|
||||
}
|
||||
} else {
|
||||
ArrayList<SignatureSourcer> params = sigSourcer.getParameters();
|
||||
|
||||
for(int i = 0; i < params.size(); i++) {
|
||||
if (i > 0) {
|
||||
mOutput.write(", ");
|
||||
}
|
||||
mOutput.write("%s arg%d", params.get(i).toString(), i);
|
||||
}
|
||||
}
|
||||
mOutput.write(")");
|
||||
|
||||
// output throwable exceptions
|
||||
if (mExceptions != null && mExceptions.length > 0) {
|
||||
mOutput.write(" throws ");
|
||||
|
||||
for (int i = 0; i < mExceptions.length; i++) {
|
||||
if (i > 0) {
|
||||
mOutput.write(", ");
|
||||
}
|
||||
mOutput.write(mExceptions[i].replace('/', '.'));
|
||||
}
|
||||
}
|
||||
|
||||
mOutput.write(" {\n");
|
||||
|
||||
mNeedDeclaration = false;
|
||||
}
|
||||
|
||||
public void visitCode() {
|
||||
writeHeader();
|
||||
|
||||
// write the stub itself
|
||||
mOutput.write("throw new RuntimeException(\"Stub\");");
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
writeHeader();
|
||||
mOutput.write("\n}\n");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
mOutput.write("@%s", desc);
|
||||
return new AnnotationSourcer(mOutput);
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
// pass
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
mOutput.write("%s /* non-standard method attribute */ ", attr.type);
|
||||
}
|
||||
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitInsn(int opcode) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitLabel(Label label) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitLdcInsn(Object cst) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitLocalVariable(String name, String desc, String signature,
|
||||
Label start, Label end, int index) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
// pass
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
// pass
|
||||
}
|
||||
|
||||
}
|
||||
57
tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
Normal file
57
tools/mkstubs/src/com/android/mkstubs/sourcer/Output.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.sourcer;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Output {
|
||||
|
||||
private final Writer mWriter;
|
||||
|
||||
public Output(Writer writer) {
|
||||
mWriter = writer;
|
||||
}
|
||||
|
||||
public void write(String format, Object... args) {
|
||||
try {
|
||||
mWriter.write(String.format(format, args));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(char c) {
|
||||
write(Character.toString(c));
|
||||
}
|
||||
|
||||
public void write(StringBuilder sb) {
|
||||
write(sb.toString());
|
||||
}
|
||||
|
||||
|
||||
public String decodeDesc(String desc) {
|
||||
return Type.getType(desc).getClassName();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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.sourcer;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.signature.SignatureReader;
|
||||
import org.objectweb.asm.signature.SignatureVisitor;
|
||||
import org.objectweb.asm.signature.SignatureWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 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. it is (parameters)return-type where
|
||||
* we want to generate "return-type method-name (parameters)".
|
||||
* So in this case the return-type and parameters are not output directly
|
||||
* but are instead accumulated in internal variables.
|
||||
*/
|
||||
class SignatureSourcer implements SignatureVisitor {
|
||||
|
||||
/**
|
||||
* Buffer used to construct the signature.
|
||||
*/
|
||||
private final StringBuilder mBuf = new StringBuilder();
|
||||
|
||||
/**
|
||||
* Buffer used to construct the formals signature.
|
||||
*/
|
||||
private final StringBuilder mFormalsBuf = new StringBuilder();
|
||||
|
||||
/**
|
||||
* Indicates if the signature is currently processing formal type parameters.
|
||||
*/
|
||||
private boolean mWritingFormals;
|
||||
|
||||
/**
|
||||
* Stack used to keep track of class types that have arguments. Each element
|
||||
* of this stack is a boolean encoded in one bit. The top of the stack is
|
||||
* the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
|
||||
* /2.
|
||||
*/
|
||||
private int mArgumentStack;
|
||||
|
||||
private SignatureSourcer mReturnType;
|
||||
|
||||
private SignatureSourcer mSuperClass;
|
||||
|
||||
private ArrayList<SignatureSourcer> mParameters = new ArrayList<SignatureSourcer>();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new {@link SignatureWriter} object.
|
||||
*/
|
||||
public SignatureSourcer() {
|
||||
}
|
||||
|
||||
private StringBuilder getBuf() {
|
||||
if (mWritingFormals) {
|
||||
return mFormalsBuf;
|
||||
} else {
|
||||
return mBuf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the whole signature type when called by
|
||||
* {@link SignatureReader#acceptType(SignatureVisitor)} or just the formals if
|
||||
* called by {@link SignatureReader#accept(SignatureVisitor)}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return mBuf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be non-null if a return type was processed
|
||||
* by {@link SignatureReader#accept(SignatureVisitor)}
|
||||
*/
|
||||
public SignatureSourcer getReturnType() {
|
||||
return mReturnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be non-empty if a parameters were processed
|
||||
* by {@link SignatureReader#accept(SignatureVisitor)}
|
||||
*/
|
||||
public ArrayList<SignatureSourcer> getParameters() {
|
||||
return mParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the signature contains formal type parameters, which are available
|
||||
* via {@link #formalsToString()} after calling {@link SignatureReader#accept(SignatureVisitor)}
|
||||
*/
|
||||
public boolean hasFormalsContent() {
|
||||
return mFormalsBuf.length() > 0;
|
||||
}
|
||||
|
||||
public String formalsToString() {
|
||||
return mFormalsBuf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be non-null if a super class was processed
|
||||
* by {@link SignatureReader#accept(SignatureVisitor)}
|
||||
*/
|
||||
public SignatureSourcer getSuperClass() {
|
||||
return mSuperClass;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Implementation of the SignatureVisitor interface
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public void visitFormalTypeParameter(final String name) {
|
||||
if (!mWritingFormals) {
|
||||
mWritingFormals = true;
|
||||
getBuf().append('<');
|
||||
} else {
|
||||
getBuf().append(", ");
|
||||
}
|
||||
getBuf().append(name);
|
||||
getBuf().append(" extends ");
|
||||
}
|
||||
|
||||
public SignatureVisitor visitClassBound() {
|
||||
// we don't differentiate between visiting a sub class or interface type
|
||||
return this;
|
||||
}
|
||||
|
||||
public SignatureVisitor visitInterfaceBound() {
|
||||
// we don't differentiate between visiting a sub class or interface type
|
||||
return this;
|
||||
}
|
||||
|
||||
public SignatureVisitor visitSuperclass() {
|
||||
endFormals();
|
||||
SignatureSourcer sourcer = new SignatureSourcer();
|
||||
assert mSuperClass == null;
|
||||
mSuperClass = sourcer;
|
||||
return sourcer;
|
||||
}
|
||||
|
||||
public SignatureVisitor visitInterface() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SignatureVisitor visitParameterType() {
|
||||
endFormals();
|
||||
SignatureSourcer sourcer = new SignatureSourcer();
|
||||
mParameters.add(sourcer);
|
||||
return sourcer;
|
||||
}
|
||||
|
||||
public SignatureVisitor visitReturnType() {
|
||||
endFormals();
|
||||
SignatureSourcer sourcer = new SignatureSourcer();
|
||||
assert mReturnType == null;
|
||||
mReturnType = sourcer;
|
||||
return sourcer;
|
||||
}
|
||||
|
||||
public SignatureVisitor visitExceptionType() {
|
||||
getBuf().append('^');
|
||||
return this;
|
||||
}
|
||||
|
||||
public void visitBaseType(final char descriptor) {
|
||||
getBuf().append(Type.getType(Character.toString(descriptor)).getClassName());
|
||||
}
|
||||
|
||||
public void visitTypeVariable(final String name) {
|
||||
getBuf().append(name.replace('/', '.'));
|
||||
}
|
||||
|
||||
public SignatureVisitor visitArrayType() {
|
||||
getBuf().append('[');
|
||||
return this;
|
||||
}
|
||||
|
||||
public void visitClassType(final String name) {
|
||||
getBuf().append(name.replace('/', '.'));
|
||||
mArgumentStack *= 2;
|
||||
}
|
||||
|
||||
public void visitInnerClassType(final String name) {
|
||||
endArguments();
|
||||
getBuf().append('.');
|
||||
getBuf().append(name.replace('/', '.'));
|
||||
mArgumentStack *= 2;
|
||||
}
|
||||
|
||||
public void visitTypeArgument() {
|
||||
if (mArgumentStack % 2 == 0) {
|
||||
++mArgumentStack;
|
||||
getBuf().append('<');
|
||||
} else {
|
||||
getBuf().append(", ");
|
||||
}
|
||||
getBuf().append('*');
|
||||
}
|
||||
|
||||
public SignatureVisitor visitTypeArgument(final char wildcard) {
|
||||
if (mArgumentStack % 2 == 0) {
|
||||
++mArgumentStack;
|
||||
getBuf().append('<');
|
||||
} else {
|
||||
getBuf().append(", ");
|
||||
}
|
||||
if (wildcard != '=') {
|
||||
if (wildcard == '+') {
|
||||
getBuf().append("? extends ");
|
||||
} else if (wildcard == '-') {
|
||||
getBuf().append("? super ");
|
||||
} else {
|
||||
// can this happen?
|
||||
getBuf().append(wildcard);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
endArguments();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Utility methods
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ends the formal type parameters section of the signature.
|
||||
*/
|
||||
private void endFormals() {
|
||||
if (mWritingFormals) {
|
||||
getBuf().append('>');
|
||||
mWritingFormals = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the type arguments of a class or inner class type.
|
||||
*/
|
||||
private void endArguments() {
|
||||
if (mArgumentStack % 2 != 0) {
|
||||
getBuf().append('>');
|
||||
}
|
||||
mArgumentStack /= 2;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user