upload android base code part7

This commit is contained in:
August 2018-08-08 18:09:17 +08:00
parent 4e516ec6ed
commit 841ae54672
25229 changed files with 1709508 additions and 0 deletions

View file

@ -0,0 +1 @@
mstubs

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="FIELD_NAME_PREFIX" value="m" />
<option name="STATIC_FIELD_NAME_PREFIX" value="s" />
<option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="com.android" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />
<option name="JD_ADD_BLANK_AFTER_RETURN" value="true" />
<option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
<option name="WRAP_COMMENTS" value="true" />
<JavaCodeStyleSettings>
<option name="CLASS_NAMES_IN_JAVADOC" value="3" />
</JavaCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="THROWS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="ASSERT_STATEMENT_WRAP" value="1" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<arrangement>
<groups>
<group>
<type>GETTERS_AND_SETTERS</type>
<order>KEEP</order>
</group>
<group>
<type>OVERRIDDEN_METHODS</type>
<order>KEEP</order>
</group>
</groups>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View file

@ -0,0 +1,9 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright (C) &amp;#36;today.year The Android Open Source Project&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10; http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License." />
<option name="keyword" value="Copyright" />
<option name="allowReplaceKeyword" value="" />
<option name="myName" value="Android" />
<option name="myLocal" value="true" />
</copyright>
</component>

View file

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="Android" />
</component>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

View file

@ -0,0 +1,14 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
</inspection_tool>
<inspection_tool class="WeakerAccess" enabled="false" level="WARNING" enabled_by_default="false">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" />
<option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="true" />
<option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" />
</inspection_tool>
</profile>
</component>

View file

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/mkstubs.iml" filepath="$PROJECT_DIR$/mkstubs.iml" />
</modules>
</component>
</project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

View file

@ -0,0 +1,28 @@
#
# 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.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_JAR_MANIFEST := manifest.txt
LOCAL_STATIC_JAVA_LIBRARIES := \
asm-5.2
LOCAL_MODULE := mkstubs
include $(BUILD_HOST_JAVA_LIBRARY)

View file

@ -0,0 +1,90 @@
2009/04/20.
-------
1- Goal
-------
MkStub is small tool that takes a given JAR and filters all the private stuff we don't want to
expose, e.g.:
- remove all private members.
- only include a subset of classes.
- exclude specific classes, fields or methods.
Each method body is replaced by the bytecode for 'throw new RuntimeException("stub");'.
--------
2- Usage
--------
To control it, you give it patterns like this:
+foo => accepts all items which signature is exactly "foo"
+foo* => accepts all items which signature starts by "foo"
-bar => rejects all items which signature is exactly "bar"
-bar* => rejects all items which signature starts by "bar"
Signatures are defined by:
- a package name, e.g. com.android.blah
- a dot followed by a class name
- a # followed by a field or method name
- an internal "Java method signature" that define parameters types and return value.
Examples of signatures:
com.android.blah
com.android.blah.MyClass
com.android.blah.MyClass$MyInnerClass
com.android.blah.MyClass#mPrivateField
com.android.blah.MyClass#getInternalStuff
com.android.blah.MyClass#getInternalStuff(Ljava/lang/String;I)V
An example of configuration file:
+com.android.blah
-com.android.blah.MyClass$MyInnerClass
-com.android.blah.MyClass#mPrivateField
-com.android.blah.MyClass#getInternalStuff(Ljava/lang/String;I)V
This would include only the indicated package yet would totally exclude the inner class
and the specific field and the method with the exact given signature.
To invoke MkStub, the syntax is:
$ java -jar mkstubs input.jar output.jar [@configfile -pattern +pattern ...]
--------------------
3- Known Limitations
--------------------
Most of the following limitations exist solely because of the short development time and
because the tool was designed to solve one task and not just to be super generic. That means
any limitation here can be easily lifted.
- The generated constructors are not proper. They do not invoke the matching super()
before the generated throw exception. Any attempt to load such a class should trigger
an error from the byte code verifier or the class loader.
- We do not currently check whether a class or method uses only included types.
Suggestion: if type x.y.z is excluded, then any field, annotation, generic type,
method parameter or return value that uses that type should generate a fatal error.
- We do not filter out private classes. Their .class will still be present in the
output (and stubbed), unless they are explicitly excluded.
This is not orthogonal to the fact that private fields and methods are automatically
excluded.
- Private fields and methods are automatically excluded. There is no command line
switch to prevent that.
- The stubbed source is always generated. For example if the output jar name is
given as ~/somedir/myfinal.jar, there will be a directory created at
~/somedir/myfinal.jar_sources that will contain the equivalent Java sources.
There is not command line switch to prevent that.
- There is no attempt to match features or behavior with DroidDoc.
--
end

View file

@ -0,0 +1 @@
Main-Class: com.android.mkstubs.Main

View file

@ -0,0 +1,99 @@
/*
* 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.Main.Logger;
import org.objectweb.asm.ClassReader;
import java.io.IOException;
import java.util.Enumeration;
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;
/**
* Analyzes an input Jar to get all the relevant classes according to the given filter.
* <p/>
* This is mostly a helper extracted for convenience. Callers will want to use
* {@link #parseInputJar(String)} followed by {@link #filter(Map, Filter, Logger)}.
*/
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<>();
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();
}
}
/**
* Filters the set of classes. Removes all classes that should not be included in the
* filter or that should be excluded. This modifies the map in-place.
*
* @param classes The in-out map of classes to examine and filter. The map is filtered
* in-place.
* @param filter A filter describing which classes to include and which ones to exclude.
* @param log
*/
void filter(Map<String, ClassReader> classes, Filter filter, Logger log) {
Set<String> keys = classes.keySet();
for(Iterator<String> it = keys.iterator(); it.hasNext(); ) {
String key = it.next();
// TODO: We *could* filter out all private classes here: classes.get(key).getAccess().
// remove if we don't keep it
if (!filter.accept(key)) {
log.debug("- Remove class " + key);
it.remove();
}
}
}
}

View file

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

@ -0,0 +1,167 @@
/*
* 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.Main.Logger;
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;
/**
* A class visitor that filters out all members (fields, methods and inner classes) that are
* either private, default-access or rejected by the {@link Filter}.
*/
class FilterClassAdapter extends ClassVisitor {
private final Logger mLog;
private final Filter mFilter;
private String mClassName;
public FilterClassAdapter(ClassVisitor writer, Filter filter, Logger log) {
super(Main.ASM_VERSION, writer);
mFilter = filter;
mLog = log;
}
@Override
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
mClassName = name;
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) {
// only accept public/protected fields
if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return null;
}
// filter on field name
String filterName = String.format("%s#%s", mClassName, name);
if (!mFilter.accept(filterName)) {
mLog.debug("- 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);
}
/**
* 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) {
// only accept public/protected methods
if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return null;
}
// filter on method name using the non-generic descriptor
String filterName = String.format("%s#%s%s", mClassName, name, desc);
if (!mFilter.accept(filterName)) {
mLog.debug("- 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)) {
mLog.debug("- 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);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// TODO produce an error if a filtered annotation type is being used
return super.visitAnnotation(desc, visible);
}
@Override
public void visitAttribute(Attribute attr) {
// pass
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
// only accept public/protected inner classes
if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return;
}
// filter on name
if (!mFilter.accept(name)) {
return;
}
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
// pass
}
@Override
public void visitSource(String source, String debug) {
// pass
}
}

View file

@ -0,0 +1,312 @@
/*
* 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 org.objectweb.asm.Opcodes;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
/**
* Main entry point of the MkStubs app.
* <p/>
* For workflow details, see {@link #process(Params)}.
*/
public class Main {
public static final int ASM_VERSION = Opcodes.ASM5;
/**
* A struct-like class to hold the various input values (e.g. command-line args)
*/
static class Params {
private String mInputJarPath;
private String mOutputJarPath;
private Filter mFilter;
private boolean mVerbose;
private boolean mDumpSource;
public Params() {
mFilter = new Filter();
}
/** Sets the name of the input jar, where to read classes from. Must not be null. */
public void setInputJarPath(String inputJarPath) {
mInputJarPath = inputJarPath;
}
/** Sets the name of the output jar, where to write classes to. Must not be null. */
public void setOutputJarPath(String outputJarPath) {
mOutputJarPath = outputJarPath;
}
/** Returns the name of the input jar, where to read classes from. */
public String getInputJarPath() {
return mInputJarPath;
}
/** Returns the name of the output jar, where to write classes to. */
public String getOutputJarPath() {
return mOutputJarPath;
}
/** Returns the current instance of the filter, the include/exclude patterns. */
public Filter getFilter() {
return mFilter;
}
/** Sets verbose mode on. Default is off. */
public void setVerbose() {
mVerbose = true;
}
/** Returns true if verbose mode is on. */
public boolean isVerbose() {
return mVerbose;
}
/** Sets dump source mode on. Default is off. */
public void setDumpSource() {
mDumpSource = true;
}
/** Returns true if source should be dumped. */
public boolean isDumpSource() {
return mDumpSource;
}
}
/** Logger that writes on stdout depending a conditional verbose mode. */
static class Logger {
private final boolean mVerbose;
public Logger(boolean verbose) {
mVerbose = verbose;
}
/** Writes to stdout only in verbose mode. */
public void debug(String msg, Object...params) {
if (mVerbose) {
System.out.println(String.format(msg, params));
}
}
/** Writes to stdout all the time. */
public void info(String msg, Object...params) {
System.out.println(String.format(msg, params));
}
}
/**
* Main entry point. Processes arguments then performs the "real" work.
*/
public static void main(String[] args) {
Main m = new Main();
try {
Params p = m.processArgs(args);
m.process(p);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Grabs command-line arguments.
* The expected arguments are:
* <ul>
* <li> The filename of the input Jar.
* <li> The filename of the output Jar.
* <li> One or more include/exclude patterns or files containing these patterns.
* See {@link #addString(Params, String)} for syntax.
* </ul>
* @throws IOException on failure to read a pattern file.
*/
private Params processArgs(String[] args) throws IOException {
Params p = new Params();
for (String arg : args) {
if (arg.startsWith("--")) {
if (arg.startsWith("--v")) {
p.setVerbose();
} else if (arg.startsWith("--s")) {
p.setDumpSource();
} else if (arg.startsWith("--h")) {
usage(null);
} else {
usage("Unknown argument: " + arg);
}
} else if (p.getInputJarPath() == null) {
p.setInputJarPath(arg);
} else if (p.getOutputJarPath() == null) {
p.setOutputJarPath(arg);
} else {
addString(p, arg);
}
}
if (p.getInputJarPath() == null && p.getOutputJarPath() == null) {
usage("Missing input or output JAR.");
}
return p;
}
/**
* Adds one pattern string to the current filter.
* The syntax must be:
* <ul>
* <li> +full_include or +prefix_include*
* <li> -full_exclude or -prefix_exclude*
* <li> @filename
* </ul>
* The input string is trimmed so any space around the first letter (-/+/@) or
* at the end is removed. Empty strings are ignored.
*
* @param p The params which filters to edit.
* @param s The string to examine.
* @throws IOException
*/
private void addString(Params p, String s) throws IOException {
if (s == null) {
return;
}
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);
}
}
}
/**
* Adds all the filter strings from the given file.
*
* @param p The params which filter to edit.
* @param osFilePath The OS path to the file containing the patterns.
* @throws IOException
*
* @see #addString(Params, String)
*/
private void addStringsFromFile(Params p, String osFilePath)
throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(osFilePath));
String line;
while ((line = br.readLine()) != null) {
addString(p, line);
}
} finally {
if (br != null) {
br.close();
}
}
}
/**
* Prints some help to stdout.
* @param error The error that generated the usage, if any. Can be null.
*/
private void usage(String error) {
if (error != null) {
System.out.println("ERROR: " + error);
}
System.out.println("Usage: mkstub [--h|--s|--v] input.jar output.jar [excluded-class @excluded-classes-file ...]");
System.out.println("Options:\n" +
" --h | --help : print this usage.\n" +
" --v | --verbose : verbose mode.\n" +
" --s | --source : dump source equivalent to modified byte code.\n\n");
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);
}
/**
* Performs the main workflow of this app:
* <ul>
* <li> Read the input Jar to get all its classes.
* <li> Filter out all classes that should not be included or that should be excluded.
* <li> Goes thru the classes, filters methods/fields and generate their source
* in a directory called "&lt;outpath_jar_path&gt;_sources"
* <li> Does the same filtering on the classes but this time generates the real stubbed
* output jar.
* </ul>
*/
private void process(Params p) throws IOException {
AsmAnalyzer aa = new AsmAnalyzer();
Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath());
Logger log = new Logger(p.isVerbose());
log.info("Classes loaded: %d", classes.size());
aa.filter(classes, p.getFilter(), log);
log.info("Classes filtered: %d", classes.size());
// dump as Java source files, mostly for debugging
if (p.isDumpSource()) {
SourceGenerator src_gen = new SourceGenerator(log);
File dst_src_dir = new File(p.getOutputJarPath() + "_sources");
dst_src_dir.mkdir();
src_gen.generateSource(dst_src_dir, classes, p.getFilter());
}
// dump the stubbed jar
StubGenerator stub_gen = new StubGenerator(log);
File dst_jar = new File(p.getOutputJarPath());
stub_gen.generateStubbedJar(dst_jar, classes, p.getFilter());
}
}

View file

@ -0,0 +1,96 @@
/*
* 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.Main.Logger;
import com.android.mkstubs.sourcer.ClassSourcer;
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.Map;
import java.util.Map.Entry;
/**
* Given a set of already filtered classes, this filters out all private members and then
* generates the Java source for the remaining classes.
* <p/>
* This is an helper extracted for convenience. Callers just need to use
* {@link #generateSource(File, Map, Filter)}.
*/
class SourceGenerator {
private Logger mLog;
public SourceGenerator(Logger log) {
mLog = log;
}
/**
* Generate source for the stubbed classes, mostly for debug purposes.
* @throws IOException
*/
public void generateSource(File baseDir,
Map<String, ClassReader> classes,
Filter filter) throws IOException {
for (Entry<String, ClassReader> entry : classes.entrySet()) {
ClassReader cr = entry.getValue();
String name = classNameToJavaPath(cr.getClassName());
try (FileWriter fw = createWriter(baseDir, name)) {
visitClassSource(fw, cr, filter);
}
}
}
FileWriter createWriter(File baseDir, String name) throws IOException {
File f = new File(baseDir, name);
f.getParentFile().mkdirs();
mLog.debug("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 visitClassSource(Writer fw, ClassReader cr, Filter filter) {
mLog.debug("Dump " + cr.getClassName());
ClassVisitor javaWriter = new ClassSourcer(new Output(fw));
ClassVisitor classFilter = new FilterClassAdapter(javaWriter, filter, mLog);
cr.accept(classFilter, 0 /*flags*/);
}
}

View file

@ -0,0 +1,113 @@
/*
* 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.Main.Logger;
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.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
/**
* Given a set of already filtered classes, this filters out all private members,
* stubs the remaining classes and then generates a Jar out of them.
* <p/>
* This is an helper extracted for convenience. Callers just need to use
* {@link #generateStubbedJar(File, Map, Filter)}.
*/
class StubGenerator {
private Logger mLog;
public StubGenerator(Logger log) {
mLog = log;
}
/**
* Generate source for the stubbed classes, mostly for debug purposes.
* @throws IOException
*/
public void generateStubbedJar(File destJar,
Map<String, ClassReader> classes,
Filter filter) throws IOException {
TreeMap<String, byte[]> all = new TreeMap<>();
for (Entry<String, ClassReader> entry : classes.entrySet()) {
ClassReader cr = entry.getValue();
byte[] b = visitClassStubber(cr, filter);
String name = classNameToEntryPath(cr.getClassName());
all.put(name, b);
}
createJar(new FileOutputStream(destJar), all);
mLog.debug("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, Filter filter) {
mLog.debug("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 classFilter = new FilterClassAdapter(stubWriter, filter, mLog);
cr.accept(classFilter, 0 /*flags*/);
return cw.toByteArray();
}
}

View file

@ -0,0 +1,111 @@
/*
* 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;
/**
* Source generator for the access fields of methods, fields and classes.
* <p/>
* Given an integer access field and a type ({@link #IS_CLASS}, {@link #IS_FIELD} or
* {@link #IS_METHOD}), the {@link #write(int, int)} method can generate a string
* desribing the access modifiers for a Java source.
*/
class AccessSourcer {
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;
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".
* <p/>
* It is up to the caller to filter extra keywords that should not be generated,
* e.g. {@link Flag#ACC_SYNTHETIC}.
*
* @param access The access mode, e.g. 33 or 18
* @param 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;
}
}
}
}

View 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.sourcer;
import com.android.mkstubs.Main;
import org.objectweb.asm.AnnotationVisitor;
/**
* An annotation visitor that generates Java source for an annotation.
*/
class AnnotationSourcer extends 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) {
super(Main.ASM_VERSION);
mOutput = output;
mOpenChar = isArray ? "[" : "(";
mCloseChar = isArray ? "]" : ")";
}
@Override
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);
}
}
@Override
public void visitEnd() {
if (mNeedClose) {
mOutput.write(mCloseChar);
}
mOutput.write("\n");
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
startOpen();
mOutput.write("@%s", name);
return this;
}
@Override
public AnnotationVisitor visitArray(String name) {
startOpen();
return new AnnotationSourcer(mOutput, true /*isArray*/);
}
@Override
public void visitEnum(String name, String desc, String value) {
mOutput.write("/* annotation enum not supported: %s */\n", name);
}
}

View file

@ -0,0 +1,151 @@
/*
* 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 com.android.mkstubs.Main;
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 writes a java source.
*/
public class ClassSourcer extends ClassVisitor {
private final Output mOutput;
private final AccessSourcer mAccessSourcer;
private String mClassName;
public ClassSourcer(Output output) {
super(Main.ASM_VERSION);
mOutput = output;
mAccessSourcer = new AccessSourcer(mOutput);
}
/* Examples:
* name = com/foo/MyClass
* signature = null (if not generic)
* superName = java/lang/Object
* interfaces = [ java/lang/Runnable ... ]
*/
@Override
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");
}
@Override
public void visitEnd() {
mOutput.write("}\n");
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
mOutput.write("@%s", desc);
return new AnnotationSourcer(mOutput);
}
@Override
public void visitAttribute(Attribute attr) {
mOutput.write("%s /* non-standard class attribute */ ", attr.type);
}
@Override
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);
}
@Override
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);
}
@Override
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.
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
// Skip outer classes.
}
@Override
public void visitSource(String source, String debug) {
// Skip source information.
}
}

View file

@ -0,0 +1,80 @@
/*
* 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 com.android.mkstubs.Main;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.signature.SignatureReader;
/**
* A field visitor that generates Java source defining a field.
*/
class FieldSourcer extends 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) {
super(Main.ASM_VERSION);
mOutput = output;
mAccess = access;
mName = name;
mDesc = desc;
mSignature = signature;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
mOutput.write("@%s", desc);
return new AnnotationSourcer(mOutput);
}
@Override
public void visitAttribute(Attribute attr) {
mOutput.write("%s /* non-standard attribute */ ", attr.type);
}
@Override
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", Type.getType(mDesc).getClassName());
} 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");
}
}

View file

@ -0,0 +1,262 @@
/*
* 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 com.android.mkstubs.Main;
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;
/**
* A method visitor that generates the Java source for a whole method.
*/
class MethodSourcer extends 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) {
super(Main.ASM_VERSION);
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;
}
@Override
public void visitCode() {
writeHeader();
// write the stub itself
mOutput.write("throw new RuntimeException(\"Stub\");");
}
@Override
public void visitEnd() {
writeHeader();
mOutput.write("\n}\n");
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
mOutput.write("@%s", desc);
return new AnnotationSourcer(mOutput);
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
// pass
return null;
}
@Override
public void visitAttribute(Attribute attr) {
mOutput.write("%s /* non-standard method attribute */ ", attr.type);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
// pass
}
@Override
public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
// pass
}
@Override
public void visitIincInsn(int var, int increment) {
// pass
}
@Override
public void visitInsn(int opcode) {
// pass
}
@Override
public void visitIntInsn(int opcode, int operand) {
// pass
}
@Override
public void visitJumpInsn(int opcode, Label label) {
// pass
}
@Override
public void visitLabel(Label label) {
// pass
}
@Override
public void visitLdcInsn(Object cst) {
// pass
}
@Override
public void visitLineNumber(int line, Label start) {
// pass
}
@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
// pass
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
// pass
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
// pass
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
// pass
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
// pass
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
// pass
return null;
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
// pass
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
// pass
}
@Override
public void visitTypeInsn(int opcode, String type) {
// pass
}
@Override
public void visitVarInsn(int opcode, int var) {
// pass
}
}

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.sourcer;
import java.io.IOException;
import java.io.Writer;
/**
* An {@link Output} objects is an helper to write to a character stream {@link Writer}.
* <p/>
* It provide some helper methods to the various "sourcer" classes from this package
* to help them write to the underlying stream.
*/
public class Output {
private final Writer mWriter;
/**
* Creates a new {@link Output} object that wraps the given {@link Writer}.
* <p/>
* The caller is responsible of opening and closing the {@link Writer}.
*
* @param writer The writer to write to. Could be a file, a string, etc.
*/
public Output(Writer writer) {
mWriter = writer;
}
/**
* Writes a formatted string to the writer.
*
* @param format The format string.
* @param args The arguments for the format string.
*
* @see String#format(String, Object...)
*/
public void write(String format, Object... args) {
try {
mWriter.write(String.format(format, args));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Writes a single character to the writer.
*
* @param c The character to write.
*/
public void write(char c) {
write(Character.toString(c));
}
/**
* Writes a {@link StringBuilder} to the writer.
*
* @param sb The {@link StringBuilder#toString()} method is used to ge the string to write.
*/
public void write(StringBuilder sb) {
write(sb.toString());
}
}

View file

@ -0,0 +1,307 @@
/*
* 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 com.android.mkstubs.Main;
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;
/**
* A signature visitor that can be used to generate Java source corresponding to
* various types of signatures.
* <p/>
* Terminology: a "signature" is a type descriptor for generics. There are different types
* of signatures depending on the context where they are used, e.g. method declarations,
* method parameters, class declarations, etc..
* <p/>
* Note: most of the implementation is a duplicate of ASM's SignatureWriter with some
* slight variations.
* <p/>
* Note: When processing a method's signature, the signature order is the reverse of the source
* order, e.g. the signature is written as "(parameters)return-type" where we want to generate
* "return-type method-name (parameters)". To handle this case, the return-type and parameters
* are <em>not</em> output directly but are instead accumulated in internal variables that you can
* get later using {@link #getReturnType()}, {@link #getParameters()}, {@link #getSuperClass()}
* and {@link #formalsToString()}.
*/
class SignatureSourcer extends 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;
/**
* {@link SignatureSourcer} generated when parsing the return type of <em>this</em>
* signature. Initially null.
*/
private SignatureSourcer mReturnType;
/**
* {@link SignatureSourcer} generated when parsing the super class of <em>this</em>
* signature. Initially null.
*/
private SignatureSourcer mSuperClass;
/**
* {@link SignatureSourcer}s for each parameters generated when parsing the method parameters
* of <em>this</em> signature. Initially empty but not null.
*/
private ArrayList<SignatureSourcer> mParameters = new ArrayList<>();
/**
* Constructs a new {@link SignatureWriter} object.
*/
public SignatureSourcer() {
super(Main.ASM_VERSION);
}
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
// ------------------------------------------------------------------------
@Override
public void visitFormalTypeParameter(final String name) {
if (!mWritingFormals) {
mWritingFormals = true;
getBuf().append('<');
} else {
getBuf().append(", ");
}
getBuf().append(name);
getBuf().append(" extends ");
}
@Override
public SignatureVisitor visitClassBound() {
// we don't differentiate between visiting a sub class or interface type
return this;
}
@Override
public SignatureVisitor visitInterfaceBound() {
// we don't differentiate between visiting a sub class or interface type
return this;
}
@Override
public SignatureVisitor visitSuperclass() {
endFormals();
SignatureSourcer sourcer = new SignatureSourcer();
assert mSuperClass == null;
mSuperClass = sourcer;
return sourcer;
}
@Override
public SignatureVisitor visitInterface() {
return this;
}
@Override
public SignatureVisitor visitParameterType() {
endFormals();
SignatureSourcer sourcer = new SignatureSourcer();
mParameters.add(sourcer);
return sourcer;
}
@Override
public SignatureVisitor visitReturnType() {
endFormals();
SignatureSourcer sourcer = new SignatureSourcer();
assert mReturnType == null;
mReturnType = sourcer;
return sourcer;
}
@Override
public SignatureVisitor visitExceptionType() {
getBuf().append('^');
return this;
}
@Override
public void visitBaseType(final char descriptor) {
getBuf().append(Type.getType(Character.toString(descriptor)).getClassName());
}
@Override
public void visitTypeVariable(final String name) {
getBuf().append(name.replace('/', '.'));
}
@Override
public SignatureVisitor visitArrayType() {
getBuf().append('[');
return this;
}
@Override
public void visitClassType(final String name) {
getBuf().append(name.replace('/', '.'));
mArgumentStack *= 2;
}
@Override
public void visitInnerClassType(final String name) {
endArguments();
getBuf().append('.');
getBuf().append(name.replace('/', '.'));
mArgumentStack *= 2;
}
@Override
public void visitTypeArgument() {
if (mArgumentStack % 2 == 0) {
++mArgumentStack;
getBuf().append('<');
} else {
getBuf().append(", ");
}
getBuf().append('*');
}
@Override
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;
}
@Override
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;
}
}

View file

@ -0,0 +1,88 @@
/*
* 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 com.android.mkstubs.Main;
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;
/**
* A class visitor that generates stubs for all methods of the visited class.
* Everything else is passed as-is.
*/
public class ClassStubber extends ClassVisitor {
public ClassStubber(ClassVisitor cv) {
super(Main.ASM_VERSION, 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);
}
}

View file

@ -0,0 +1,187 @@
/*
* 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 com.android.mkstubs.Main;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A method visitor that generates a code stub for the visited method.
* <p/>
* Annotations and parameters are passed as-is.
* All other code is replaced by the following:
* <pre>throw new RuntimeException("stub");</pre>
* Note that constructors rewritten this way will probably fail with the runtime bytecode
* verifier since no call to <code>super</code> is generated.
*/
public class MethodStubber extends MethodVisitor {
public MethodStubber(MethodVisitor mw,
int access, String name, String desc, String signature, String[] exceptions) {
super(Main.ASM_VERSION, 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
false);
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, boolean itf) {
// 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
}
}

View file

@ -0,0 +1,33 @@
/*
* 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.junit.After;
import org.junit.Before;
public class FilterClassAdapterTest {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
}

View 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;
import com.android.mkstubs.Main.Logger;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.ClassReader;
import java.io.StringWriter;
/**
*
*/
public class SourceGeneratorTest {
private SourceGenerator mGen;
@Before
public void setUp() throws Exception {
mGen = new SourceGenerator(new Logger(false));
}
@After
public void tearDown() throws Exception {
}
@Test
public void testDumpClass() throws Exception {
StringWriter sw = new StringWriter();
ClassReader cr = new ClassReader("data/TestBaseClass");
mGen.visitClassSource(sw, cr, new Filter());
String s = sw.toString();
Assert.assertNotNull(s);
}
}

View file

@ -0,0 +1,63 @@
/*
* 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.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.Opcodes;
import java.io.StringWriter;
public class AccessSourcerTest {
private StringWriter mWriter;
private AccessSourcer mSourcer;
@Before
public void setUp() throws Exception {
mWriter = new StringWriter();
mSourcer = new AccessSourcer(new Output(mWriter));
}
@After
public void tearDown() throws Exception {
mWriter = null;
mSourcer = null;
}
@Test
public void testAbstractPublic() throws Exception {
mSourcer.write(Opcodes.ACC_ABSTRACT | Opcodes.ACC_PUBLIC, AccessSourcer.IS_CLASS);
String s = mWriter.toString();
Assert.assertEquals("public abstract", s);
}
@Test
public void testPrivateFinalStatic() throws Exception {
mSourcer.write(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC,
AccessSourcer.IS_METHOD);
String s = mWriter.toString();
Assert.assertEquals("private static final", s);
}
}

View 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.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.ClassReader;
import java.io.StringWriter;
/**
*
*/
public class ClassSourcerTest extends TestHelper {
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
@Test
public void testBaseClassSource() throws Exception {
StringWriter sw = new StringWriter();
ClassReader cr = new ClassReader("data/TestBaseClass");
ClassSourcer jw = new ClassSourcer(new Output(sw));
cr.accept(jw, 0);
assertSourceEquals(
"package data;\n" +
"public class TestBaseClass extends java.lang.Object implements java.lang.Runnable {\n" +
"\n" +
" private final java.lang.String mArg;\n" +
" \n" +
" public TestBaseClass() {\n" +
" throw new RuntimeException(\"Stub\");" +
" }\n" +
" public TestBaseClass(java.lang.String arg0) {\n" +
" throw new RuntimeException(\"Stub\");" +
" }\n" +
" public java.lang.String getArg() {\n" +
" throw new RuntimeException(\"Stub\");" +
" }\n" +
" public void run() {\n" +
" throw new RuntimeException(\"Stub\");" +
" }\n" +
"}",
sw.toString());
}
@Test
public void testInnerClassSource() throws Exception {
StringWriter sw = new StringWriter();
ClassReader cr = new ClassReader("data/TestInnerClass");
ClassSourcer jw = new ClassSourcer(new Output(sw));
cr.accept(jw, 0);
assertSourceEquals(
"package data;\n" +
"public class TestInnerClass extends java.lang.Object {\n" +
" private final java.lang.String mArg;\n" +
" public TestInnerClass() {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public TestInnerClass(java.lang.String arg0) {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public java.lang.String getArg() {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public data.TestInnerClass$InnerPubClass getInnerPubClass() {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
"}",
sw.toString());
}
@Test
public void testTemplateClassSource() throws Exception {
StringWriter sw = new StringWriter();
ClassReader cr = new ClassReader("data/TestTemplateClass");
ClassSourcer jw = new ClassSourcer(new Output(sw));
cr.accept(jw, 0);
assertSourceEquals(
"package data;\n" +
"public class TestTemplateClass<T extends java.io.InputStream, U extends java.lang.Object> extends java.lang.Object {\n" +
" private final java.util.Map<T, U> mMap_T_U;\n" +
" public java.util.Map<java.util.ArrayList<T>, java.util.Map<java.lang.String, java.util.ArrayList<U>>> mMap_T_S_U;\n" +
" public TestTemplateClass() {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public java.util.Map<T, U> getMap_T_U() {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public java.util.Map<java.util.ArrayList<T>, java.util.Map<java.lang.String, java.util.ArrayList<U>>> getMap_T_S_U() {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public void draw(java.util.List<? extends org.w3c.dom.css.Rect> arg0) {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public static <T extends java.lang.Comparable<? super T>> void sort(java.util.List<T> arg0) {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
" public <X extends T, Y extends java.lang.Object> void getMap(java.util.List<T> arg0, java.util.Map<T, U> arg1, java.util.Map<X, java.util.Set<? super Y>> arg2) {\n" +
" throw new RuntimeException(\"Stub\");\n" +
" }\n" +
"}",
sw.toString());
}
}

View file

@ -0,0 +1,75 @@
/*
* 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.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.Opcodes;
import java.io.StringWriter;
/**
*
*/
public class FieldSourcerTest {
private StringWriter mWriter;
@Before
public void setUp() throws Exception {
mWriter = new StringWriter();
}
@After
public void tearDown() throws Exception {
mWriter = null;
}
@Test
public void testStringField() throws Exception {
FieldSourcer fs = new FieldSourcer(new Output(mWriter),
Opcodes.ACC_PUBLIC, // access
"mArg", // name
"Ljava/lang/String;", // desc
null // signature
);
fs.visitEnd();
String s = mWriter.toString();
Assert.assertEquals("public java.lang.String mArg;\n", s);
}
@Test
public void testTemplateTypeField() throws Exception {
FieldSourcer fs = new FieldSourcer(new Output(mWriter),
Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, // access
"mList", // name
"Ljava/util/ArrayList;", // desc
"Ljava/util/ArrayList<Ljava/lang/String;>;" // signature
);
fs.visitEnd();
String s = mWriter.toString();
Assert.assertEquals("private final java.util.ArrayList<java.lang.String> mList;\n", s);
}
}

View file

@ -0,0 +1,94 @@
/*
* 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.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.Opcodes;
import java.io.StringWriter;
/**
*
*/
public class MethodSourcerTest extends TestHelper {
private StringWriter mWriter;
private Output mOutput;
@Before
public void setUp() throws Exception {
mWriter = new StringWriter();
mOutput = new Output(mWriter);
}
@After
public void tearDown() throws Exception {
mWriter = null;
}
@Test
public void testVoid() {
MethodSourcer m = new MethodSourcer(mOutput,
"foo", //classname
Opcodes.ACC_PUBLIC, //access
"testVoid", //name
"()V", //desc
null, //signature
null); //exception
m.visitEnd();
assertSourceEquals(
"public void testVoid() { }",
mWriter.toString());
}
@Test
public void testVoidThrow() {
MethodSourcer m = new MethodSourcer(mOutput,
"foo", //classname
Opcodes.ACC_PUBLIC, //access
"testVoid", //name
"()V", //desc
null, //signature
new String[] { "java/lang/Exception" }); //exception
m.visitEnd();
assertSourceEquals(
"public void testVoid() throws java.lang.Exception { }",
mWriter.toString());
}
@Test
public void testReturnMap() {
MethodSourcer m = new MethodSourcer(mOutput,
"foo", //classname
Opcodes.ACC_PUBLIC, //access
"getMap_T_U", //name
"()Ljava/util/Map;", //desc
"()Ljava/util/Map<TT;TU;>;", //signature
null); //exception
m.visitEnd();
assertSourceEquals(
"public java.util.Map<T, U> getMap_T_U() { }",
mWriter.toString());
}
}

View file

@ -0,0 +1,146 @@
/*
* 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.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.signature.SignatureReader;
import java.util.ArrayList;
/**
*
*/
public class SignatureSourcerTest {
private SignatureSourcer mSourcer;
@Before
public void setUp() throws Exception {
mSourcer = new SignatureSourcer();
}
@After
public void tearDown() throws Exception {
}
@Test
public void testReturnMapNoArgs() {
SignatureReader reader = new SignatureReader(
"()Ljava/util/Map<Ljava/util/ArrayList<TT;>;Ljava/util/Map<Ljava/lang/String;Ljava/util/ArrayList<TU;>;>;>;");
reader.accept(mSourcer);
String result = mSourcer.getReturnType().toString();
Assert.assertEquals(
"java.util.Map<java.util.ArrayList<T>, java.util.Map<java.lang.String, java.util.ArrayList<U>>>",
result);
}
@Test
public void testReturnVoid() {
SignatureReader reader = new SignatureReader(
"(Ljava/util/List<+Lorg/w3c/dom/css/Rect;>;)V");
reader.accept(mSourcer);
String result = mSourcer.getReturnType().toString();
Assert.assertEquals(
"void",
result);
}
@Test
public void testSimpleArg() {
SignatureReader reader = new SignatureReader(
"(Ljava/util/List<+Lorg/w3c/dom/css/Rect;>;)V");
reader.accept(mSourcer);
ArrayList<SignatureSourcer> params = mSourcer.getParameters();
Assert.assertNotNull(params);
String[] array = toStringArray(params);
Assert.assertArrayEquals(
new String[] { "java.util.List<? extends org.w3c.dom.css.Rect>" },
array);
}
@Test
public void testFormalParameters1() {
SignatureReader reader = new SignatureReader("<X:TT;Y:Ljava/lang/Object;>()V");
reader.accept(mSourcer);
Assert.assertTrue(mSourcer.hasFormalsContent());
String result = mSourcer.formalsToString();
Assert.assertEquals(
"<X extends T, Y extends java.lang.Object>",
result);
}
@Test
public void testFormalParameters2() {
SignatureReader reader = new SignatureReader("<T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V");
reader.accept(mSourcer);
Assert.assertTrue(mSourcer.hasFormalsContent());
String result = mSourcer.formalsToString();
Assert.assertEquals(
"<T extends java.lang.Comparable<? super T>>",
result);
}
@Test
public void testManyArgs() {
SignatureReader reader = new SignatureReader(
"<X:TT;Y:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Map<TT;TU;>;Ljava/util/Map<TX;Ljava/util/Set<-TY;>;>;)V");
reader.accept(mSourcer);
Assert.assertTrue(mSourcer.hasFormalsContent());
String formals = mSourcer.formalsToString();
Assert.assertEquals(
"<X extends T, Y extends java.lang.Object>",
formals);
String result = mSourcer.getReturnType().toString();
Assert.assertEquals(
"void",
result);
ArrayList<SignatureSourcer> params = mSourcer.getParameters();
Assert.assertNotNull(params);
String[] array = toStringArray(params);
Assert.assertArrayEquals(
new String[] { "java.util.List<T>",
"java.util.Map<T, U>",
"java.util.Map<X, java.util.Set<? super Y>>" },
array);
}
private String[] toStringArray(ArrayList<?> params) {
String[] array = new String[params.size()];
for (int i = 0; i < params.size(); i++) {
array[i] = params.get(i).toString();
}
return array;
}
}

View file

@ -0,0 +1,37 @@
/*
* 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.junit.Assert;
/**
*
*/
abstract class TestHelper {
/**
* Test source equality after normalizing all whitespace.
*/
public void assertSourceEquals(String expected, String actual) {
String en = expected.replaceAll("[\\s]+", " ").trim();
String an = actual.replaceAll( "[\\s]+", " ").trim();
Assert.assertEquals(
String.format("Source comparison failure: expected:<%s> but was:<%s>", expected, actual),
en, an);
}
}

View file

@ -0,0 +1,42 @@
/*
* 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 data;
/**
*
*/
public class TestBaseClass implements Runnable {
private final String mArg;
public TestBaseClass() {
throw new RuntimeException("Stub");
}
public TestBaseClass(String arg) {
mArg = arg;
}
public String getArg() {
return mArg;
}
@SuppressWarnings("unused")
public void run() {
}
}

View file

@ -0,0 +1,52 @@
/*
* 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 data;
/**
*
*/
public class TestInnerClass {
private final String mArg;
private class InnerPrivClass {
}
public class InnerPubClass {
}
private static final class InnerStaticClass {
}
public TestInnerClass() {
mArg = null;
}
public TestInnerClass(String arg) {
mArg = arg;
}
public String getArg() {
return mArg;
}
public InnerPubClass getInnerPubClass() {
return new InnerPubClass();
}
}

View file

@ -0,0 +1,55 @@
/*
* 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 data;
import org.w3c.dom.css.Rect;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
*/
public class TestTemplateClass<T extends InputStream, U> {
private final Map<T, U> mMap_T_U = null;
public Map<ArrayList<T>, Map<String, ArrayList<U>>> mMap_T_S_U = null;
public TestTemplateClass() {
}
public Map<T, U> getMap_T_U() {
return mMap_T_U;
}
public Map<ArrayList<T>, Map<String, ArrayList<U>>> getMap_T_S_U() {
return mMap_T_S_U;
}
public void draw(List<? extends Rect> shape) {
}
public static <T extends Comparable<? super T>> void sort(List<T> list) {
}
public <X extends T, Y> void getMap(List<T> list, Map<T, U> tu, Map<X, Set<? super Y>> xy) {
}
}