Removes SmaliDebugging

- fixes #1061
This commit is contained in:
Connor Tumbleson
2016-02-27 20:20:53 -05:00
parent 1ff3a375d4
commit cd852cd658
25 changed files with 39 additions and 1229 deletions

View File

@ -36,10 +36,8 @@ dependencies {
compile project(':brut.j.dir'),
project(':brut.j.util'),
project(':brut.j.common'),
project(':brut.apktool.smali:util'),
project(':brut.apktool.smali:dexlib2'),
project(':brut.apktool.smali:baksmali'),
project(':brut.apktool.smali:smali'),
depends.baksmali,
depends.smali,
depends.snakeyaml,
depends.xmlpull,
depends.guava,

View File

@ -16,7 +16,6 @@
package brut.androlib;
import brut.androlib.java.AndrolibJava;
import brut.androlib.meta.MetaInfo;
import brut.androlib.meta.UsesFramework;
import brut.androlib.res.AndrolibResources;
@ -80,8 +79,8 @@ public class Androlib {
}
}
public void decodeSourcesSmali(File apkFile, File outDir, String filename, boolean debug, String debugLinePrefix,
boolean bakdeb, int api) throws AndrolibException {
public void decodeSourcesSmali(File apkFile, File outDir, String filename, boolean bakdeb, int api)
throws AndrolibException {
try {
File smaliDir;
if (filename.equalsIgnoreCase("classes.dex")) {
@ -92,18 +91,12 @@ public class Androlib {
OS.rmdir(smaliDir);
smaliDir.mkdirs();
LOGGER.info("Baksmaling " + filename + "...");
SmaliDecoder.decode(apkFile, smaliDir, filename, debug, debugLinePrefix, bakdeb, api);
SmaliDecoder.decode(apkFile, smaliDir, filename, bakdeb, api);
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}
public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug)
throws AndrolibException {
LOGGER.info("Decoding Java sources...");
new AndrolibJava().decode(apkFile, outDir);
}
public void decodeManifestRaw(ExtFile apkFile, File outDir)
throws AndrolibException {
try {
@ -299,9 +292,7 @@ public class Androlib {
public void buildSources(File appDir)
throws AndrolibException {
if (!buildSourcesRaw(appDir, "classes.dex")
&& !buildSourcesSmali(appDir, "smali", "classes.dex")
&& !buildSourcesJava(appDir)) {
if (!buildSourcesRaw(appDir, "classes.dex") && !buildSourcesSmali(appDir, "smali", "classes.dex")) {
LOGGER.warning("Could not find sources");
}
}
@ -316,9 +307,7 @@ public class Androlib {
if (name.startsWith("smali_")) {
String filename = name.substring(name.indexOf("_") + 1) + ".dex";
if (!buildSourcesRaw(appDir, filename)
&& !buildSourcesSmali(appDir, name, filename)
&& !buildSourcesJava(appDir)) {
if (!buildSourcesRaw(appDir, filename) && !buildSourcesSmali(appDir, name, filename)) {
LOGGER.warning("Could not find sources");
}
}
@ -372,25 +361,7 @@ public class Androlib {
if (apkOptions.forceBuildAll || isModified(smaliDir, dex)) {
LOGGER.info("Smaling " + folder + " folder into " + filename +"...");
dex.delete();
SmaliBuilder.build(smaliDir, dex, apkOptions.debugMode);
}
return true;
}
public boolean buildSourcesJava(File appDir)
throws AndrolibException {
File javaDir = new File(appDir, "src");
if (!javaDir.exists()) {
return false;
}
File dex = new File(appDir, APK_DIRNAME + "/classes.dex");
if (! apkOptions.forceBuildAll) {
LOGGER.info("Checking whether sources has changed...");
}
if (apkOptions.forceBuildAll || isModified(javaDir, dex)) {
LOGGER.info("Building java sources...");
dex.delete();
new AndrolibJava().build(javaDir, dex);
SmaliBuilder.build(smaliDir, dex);
}
return true;
}
@ -487,10 +458,6 @@ public class Androlib {
File apkDir = new File(appDir, APK_DIRNAME);
if (apkOptions.debugMode) {
ResXmlPatcher.removeApplicationDebugTag(new File(apkDir,"AndroidManifest.xml"));
}
if (apkOptions.forceBuildAll || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir),
newFiles(APK_MANIFEST_FILENAMES, apkDir))) {
LOGGER.info("Building AndroidManifest.xml...");

View File

@ -128,10 +128,7 @@ public class ApkDecoder {
mAndrolib.decodeSourcesRaw(mApkFile, outDir, "classes.dex");
break;
case DECODE_SOURCES_SMALI:
mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex", mDebug, mDebugLinePrefix, mBakDeb, mApi);
break;
case DECODE_SOURCES_JAVA:
mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug);
mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex", mBakDeb, mApi);
break;
}
}
@ -147,10 +144,7 @@ public class ApkDecoder {
mAndrolib.decodeSourcesRaw(mApkFile, outDir, file);
break;
case DECODE_SOURCES_SMALI:
mAndrolib.decodeSourcesSmali(mApkFile, outDir, file, mDebug, mDebugLinePrefix, mBakDeb, mApi);
break;
case DECODE_SOURCES_JAVA:
mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug);
mAndrolib.decodeSourcesSmali(mApkFile, outDir, file, mBakDeb, mApi);
break;
}
}
@ -167,7 +161,7 @@ public class ApkDecoder {
}
public void setDecodeSources(short mode) throws AndrolibException {
if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI && mode != DECODE_SOURCES_JAVA) {
if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI) {
throw new AndrolibException("Invalid decode sources mode: " + mode);
}
mDecodeSources = mode;
@ -180,11 +174,6 @@ public class ApkDecoder {
mDecodeResources = mode;
}
public void setDebugMode(boolean debug) {
LOGGER.warning("SmaliDebugging has been deprecated. It will be removed in Apktool 2.1 - https://github.com/iBotPeaches/Apktool/issues/1061");
mDebug = debug;
}
public void setAnalysisMode(boolean mode, boolean pass) throws AndrolibException{
mAnalysisMode = mode;
@ -208,10 +197,6 @@ public class ApkDecoder {
}
}
public void setDebugLinePrefix(String debugLinePrefix) {
mDebugLinePrefix = debugLinePrefix;
}
public void setBaksmaliDebugMode(boolean bakdeb) {
mBakDeb = bakdeb;
}
@ -288,7 +273,6 @@ public class ApkDecoder {
public final static short DECODE_SOURCES_NONE = 0x0000;
public final static short DECODE_SOURCES_SMALI = 0x0001;
public final static short DECODE_SOURCES_JAVA = 0x0002;
public final static short DECODE_RESOURCES_NONE = 0x0100;
public final static short DECODE_RESOURCES_FULL = 0x0101;
@ -396,8 +380,6 @@ public class ApkDecoder {
private ResTable mResTable;
private short mDecodeSources = DECODE_SOURCES_SMALI;
private short mDecodeResources = DECODE_RESOURCES_FULL;
private String mDebugLinePrefix = "a=0;// ";
private boolean mDebug = false;
private boolean mForceDelete = false;
private boolean mKeepBrokenResources = false;
private boolean mBakDeb = true;

View File

@ -19,7 +19,6 @@ import java.util.Collection;
public class ApkOptions {
public boolean forceBuildAll = false;
public boolean debugMode = false;
public boolean verbose = false;
public boolean copyOriginalFiles = false;
public boolean updateFiles = false;

View File

@ -1,34 +0,0 @@
/**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* 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 brut.androlib.java;
import brut.androlib.res.util.ExtFile;
import java.io.File;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class AndrolibJava {
public void decode(ExtFile apkFile, File outDir) {
throw new UnsupportedOperationException("Not yet implemented");
}
public void build(File javaDir, File dex) {
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@ -343,10 +343,6 @@ final public class AndrolibResources {
if (apkOptions.updateFiles) {
cmd.add("-u");
}
if (apkOptions.debugMode) { // inject debuggable="true" into manifest
cmd.add("--debug-mode");
}
// force package id so that some frameworks build with correct id
// disable if user adds own aapt (can't know if they have this feature)
if (mPackageId != null && ! customAapt && ! mSharedLibrary) {

View File

@ -40,35 +40,6 @@ import java.io.IOException;
* @author Connor Tumbleson <connor.tumbleson@gmail.com>
*/
public final class ResXmlPatcher {
/**
* Removes "debug" tag from file
*
* @param file AndroidManifest file
* @throws AndrolibException
*/
public static void removeApplicationDebugTag(File file) throws AndrolibException {
if (file.exists()) {
try {
Document doc = loadDocument(file);
Node application = doc.getElementById("application");
// load attr
NamedNodeMap attr = application.getAttributes();
Node debugAttr = attr.getNamedItem("debug");
// remove application:debug
if (debugAttr != null) {
attr.removeNamedItem("debug");
}
saveDocument(file, doc);
} catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) {
}
}
}
/**
* Any @string reference in a <provider> value in AndroidManifest.xml will break on
* build, thus preventing the application from installing. This is from a bug/error

View File

@ -1,244 +0,0 @@
/**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* 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 brut.androlib.src;
import brut.androlib.AndrolibException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class DebugInjector {
private boolean areParamsInjected;
private int currParam;
private int lastParam;
public static void inject(ListIterator<String> it, StringBuilder out)
throws AndrolibException {
new DebugInjector(it, out).inject();
}
private DebugInjector(ListIterator<String> it, StringBuilder out) {
mIt = it;
mOut = out;
}
private void inject() throws AndrolibException {
String definition = nextAndAppend();
if (definition.contains(" abstract ")
|| definition.contains(" native ")) {
nextAndAppend();
return;
}
parseParamsNumber(definition);
boolean end = false;
while (!end) {
end = step();
}
}
private void parseParamsNumber(String definition) throws AndrolibException {
int pos = definition.indexOf('(');
if (pos == -1) {
throw new AndrolibException();
}
int pos2 = definition.indexOf(')', pos);
if (pos2 == -1) {
throw new AndrolibException();
}
String params = definition.substring(pos + 1, pos2);
currParam = definition.contains(" static ") ? 0 : 1;
lastParam = TypeName.listFromInternalName(params).size() + currParam - 1;
}
private void injectRemainingParams() {
areParamsInjected = true;
while(currParam <= lastParam) {
//mOut.append(".param \"p").append(currParam).append("\"\n");
currParam++;
}
}
private boolean step() {
String line = next();
if (line.isEmpty()) {
return false;
}
switch (line.charAt(0)) {
case '#':
return processComment(line);
case ':':
append(line);
return false;
case '.':
return processDirective(line);
default:
if (! areParamsInjected) {
injectRemainingParams();
}
return processInstruction(line);
}
}
private boolean processComment(String line) {
if (mFirstInstruction) {
return false;
}
Matcher m = REGISTER_INFO_PATTERN.matcher(line);
while (m.find()) {
String localName = m.group(1);
String localType = null;
switch (m.group(2)) {
case "Reference":
case "UninitRef":
case "REFERENCE":
case "Null":
case "UninitThis":
localType = "Ljava/lang/Object;";
break;
case "Boolean":
localType = "Z";
break;
case "Integer":
case "One":
case "Unknown":
localType = "I";
break;
case "Uninit":
case "Conflicted":
if (mInitializedRegisters.remove(localName)) {
mOut.append(".end local ").append(localName).append('\n');
}
continue;
case "Short":
case "PosShort":
localType = "S";
break;
case "Byte":
case "PosByte":
localType = "B";
break;
case "Char":
localType = "C";
break;
case "Float":
localType = "F";
break;
case "LongHi":
case "LongLo":
localType = "J";
break;
case "DoubleHi":
case "DoubleLo":
localType = "D";
break;
default:
System.err.println(line);
System.err.println(m.group(2));
System.err.println(m.group(3));
assert false;
}
mInitializedRegisters.add(localName);
mOut.append(".local ").append(localName).append(", ").append('"')
.append(localName).append('"').append(':').append(localType)
.append('\n');
}
return false;
}
private boolean processDirective(String line) {
String line2 = line.substring(1);
if (line2.startsWith("line ") || line2.startsWith("local ") || line2.startsWith("end local ")) {
return false;
}
if (line2.equals("prologue")) {
if (! areParamsInjected) {
injectRemainingParams();
}
return false;
}
if (line2.equals("param")) {
mOut.append(".param \"p").append(currParam++).append("\"\n");
return false;
}
if (line2.startsWith("param")) {
mOut.append(line).append("\n");
currParam++;
return false;
}
append(line);
if (line2.equals("end method")) {
return true;
}
if (line2.startsWith("annotation ") || line2.equals("sparse-switch")
|| line2.startsWith("packed-switch ")
|| line2.startsWith("array-data ")) {
while (true) {
line2 = nextAndAppend();
if (line2.startsWith(".end ")) {
break;
}
}
}
return false;
}
private boolean processInstruction(String line) {
if (mFirstInstruction) {
mOut.append(".prologue\n");
mFirstInstruction = false;
}
mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line)
.append('\n');
return false;
}
private String next() {
return mIt.next().split("//", 2)[1].trim();
}
private String nextAndAppend() {
String line = next();
append(line);
return line;
}
private void append(String append) {
mOut.append(append).append('\n');
}
private final ListIterator<String> mIt;
private final StringBuilder mOut;
private boolean mFirstInstruction = true;
private final Set<String> mInitializedRegisters = new HashSet<String>();
private static final Pattern REGISTER_INFO_PATTERN = Pattern
.compile("((?:p|v)\\d+)=\\(([^,)]+)([^)]*)\\);");
}

View File

@ -21,9 +21,7 @@ import brut.androlib.mod.SmaliMod;
import brut.androlib.res.util.ExtFile;
import brut.directory.DirectoryException;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.logging.Logger;
import org.antlr.runtime.RecognitionException;
@ -36,15 +34,13 @@ import org.jf.dexlib2.writer.io.FileDataStore;
*/
public class SmaliBuilder {
public static void build(ExtFile smaliDir, File dexFile, boolean debug)
throws AndrolibException {
new SmaliBuilder(smaliDir, dexFile, debug).build();
public static void build(ExtFile smaliDir, File dexFile) throws AndrolibException {
new SmaliBuilder(smaliDir, dexFile).build();
}
private SmaliBuilder(ExtFile smaliDir, File dexFile, boolean debug) {
private SmaliBuilder(ExtFile smaliDir, File dexFile) {
mSmaliDir = smaliDir;
mDexFile = dexFile;
mDebug = debug;
}
private void build() throws AndrolibException {
@ -84,29 +80,9 @@ public class SmaliBuilder {
List<String> lines = IOUtils.readLines(inStream);
inStream.close();
if (! mDebug) {
final String[] linesArray = lines.toArray(new String[0]);
for (int i = 1; i < linesArray.length - 1; i++) {
out.append(linesArray[i].split("//", 2)[1]).append('\n');
}
} else {
lines.remove(lines.size() - 1);
ListIterator<String> it = lines.listIterator(1);
out.append(".source \"").append(inFile.getName()).append("\"\n");
while (it.hasNext()) {
String line = it.next().split("//", 2)[1].trim();
if (line.isEmpty() || line.charAt(0) == '#' || line.startsWith(".source")) {
continue;
}
if (line.startsWith(".method ")) {
it.previous();
DebugInjector.inject(it, out);
continue;
}
out.append(line).append('\n');
}
final String[] linesArray = lines.toArray(new String[0]);
for (int i = 1; i < linesArray.length - 1; i++) {
out.append(linesArray[i].split("//", 2)[1]).append('\n');
}
try {
if (!SmaliMod.assembleSmaliFile(out.toString(),dexBuilder, false, false, inFile)) {
@ -119,7 +95,6 @@ public class SmaliBuilder {
private final ExtFile mSmaliDir;
private final File mDexFile;
private final boolean mDebug;
private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class.getName());
}

View File

@ -20,52 +20,38 @@ import brut.androlib.AndrolibException;
import org.jf.baksmali.baksmali;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.analysis.ClassPath;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
import org.jf.dexlib2.analysis.InlineMethodResolver;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class SmaliDecoder {
public static void decode(File apkFile, File outDir, String dexName, boolean debug, String debugLinePrefix,
boolean bakdeb, int api) throws AndrolibException {
new SmaliDecoder(apkFile, outDir, dexName, debug, debugLinePrefix, bakdeb, api).decode();
public static void decode(File apkFile, File outDir, String dexName, boolean bakdeb, int api)
throws AndrolibException {
new SmaliDecoder(apkFile, outDir, dexName, bakdeb, api).decode();
}
private SmaliDecoder(File apkFile, File outDir, String dexName, boolean debug, String debugLinePrefix,
boolean bakdeb, int api) {
private SmaliDecoder(File apkFile, File outDir, String dexName, boolean bakdeb, int api) {
mApkFile = apkFile;
mOutDir = outDir.toPath();
mOutDir = outDir;
mDexFile = dexName;
mDebug = debug;
mDebugLinePrefix = debugLinePrefix;
mBakDeb = bakdeb;
mApi = api;
mBakDeb = bakdeb;
mApi = api;
}
private void decode() throws AndrolibException {
try {
ClassPath.dontLoadClassPath = mDebug;
baksmaliOptions options = new baksmaliOptions();
// options
options.deodex = false;
options.outputDirectory = mOutDir.toAbsolutePath().toString();
options.outputDirectory = mOutDir.toString();
options.noParameterRegisters = false;
options.useLocalsDirective = true;
options.useSequentialLabels = true;
@ -73,21 +59,15 @@ public class SmaliDecoder {
options.addCodeOffsets = false;
options.jobs = -1;
options.noAccessorComments = false;
options.registerInfo = (mDebug ? baksmaliOptions.DIFFPRE : 0);
options.registerInfo = 0;
options.ignoreErrors = false;
options.inlineResolver = null;
options.checkPackagePrivateAccess = false;
// set jobs automatically
if (options.jobs <= 0) {
if (mDebug) {
options.jobs = 1;
} else {
options.jobs = Runtime.getRuntime().availableProcessors();
if (options.jobs > 6) {
options.jobs = 6;
}
}
options.jobs = Runtime.getRuntime().availableProcessors();
if (options.jobs > 6) {
options.jobs = 6;
}
// create the dex
@ -102,54 +82,15 @@ public class SmaliDecoder {
InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion());
}
baksmali.disassembleDexFile(dexFile,options);
if (mDebug) {
Files.walkFileTree(mOutDir, new SmaliFileVisitor());
}
baksmali.disassembleDexFile(dexFile, options);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
private final File mApkFile;
private final Path mOutDir;
private final boolean mDebug;
private final String mDebugLinePrefix;
private final File mOutDir;
private final String mDexFile;
private final boolean mBakDeb;
private final int mApi;
private class SmaliFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = file.getFileName().toString();
if (! fileName.endsWith(".smali")) {
return FileVisitResult.CONTINUE;
}
fileName = fileName.substring(0, fileName.length() - 6);
try (
BufferedReader in = Files.newBufferedReader(file, Charset.defaultCharset());
BufferedWriter out = Files.newBufferedWriter(
file.resolveSibling(fileName + ".java"), Charset.defaultCharset())
) {
TypeName type = TypeName.fromPath(mOutDir.relativize(file.resolveSibling(fileName)));
out.write("package " + type.package_ + "; class " + type.getName(true, true) + " { void a() { int a;");
out.newLine();
String line;
final String debugLinePrefix = mDebugLinePrefix;
while ((line = in.readLine()) != null) {
out.write(debugLinePrefix);
out.write(line);
out.newLine();
}
out.write("}}");
out.newLine();
}
Files.delete(file);
return FileVisitResult.CONTINUE;
}
}
}

View File

@ -1,212 +0,0 @@
/**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* 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 brut.androlib.src;
import brut.androlib.AndrolibException;
import brut.util.Duo;
import com.google.common.base.Joiner;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class TypeName {
public final String package_;
public final String type;
public final String innerType;
public final int array;
public TypeName(String type, int array) {
this(null, type, null, array);
}
public TypeName(String package_, String type, String innerType, int array) {
this.package_ = package_;
this.type = type;
this.innerType = innerType;
this.array = array;
}
public String getShortenedName() {
return getName("java.lang".equals(package_), isFileOwner());
}
public String getName() {
return getName(false, false);
}
public String getName(boolean excludePackage, boolean separateInner) {
String name = (package_ == null || excludePackage ? "" : package_ + '.')
+ type
+ (innerType != null ? (separateInner ? '$' : '.') + innerType
: "");
for (int i = 0; i < array; i++) {
name += "[]";
}
return name;
}
public String getJavaFilePath() {
return getFilePath(isFileOwner()) + ".java";
}
public String getSmaliFilePath() {
return getFilePath(true) + ".smali";
}
public String getFilePath(boolean separateInner) {
return package_.replace('.', File.separatorChar) + File.separatorChar
+ type + (separateInner && isInner() ? "$" + innerType : "");
}
public boolean isInner() {
return innerType != null;
}
public boolean isArray() {
return array != 0;
}
public boolean isFileOwner() {
if (mIsFileOwner == null) {
mIsFileOwner = true;
if (isInner()) {
char c = innerType.charAt(0);
if (c < '0' || c > '9') {
mIsFileOwner = false;
}
}
}
return mIsFileOwner;
}
@Override
public String toString() {
return getName();
}
public static TypeName fromInternalName(String internal)
throws AndrolibException {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
if (duo.m2 != internal.length()) {
throw new AndrolibException("Invalid internal name: " + internal);
}
return duo.m1;
}
public static List<TypeName> listFromInternalName(String internal)
throws AndrolibException {
List<TypeName> types = new ArrayList<TypeName>();
while (!internal.isEmpty()) {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
types.add(duo.m1);
internal = internal.substring(duo.m2);
}
return types;
}
public static TypeName fromPath(Path path) {
List<String> parts = new ArrayList<>(path.getNameCount());
for (Path p : path) {
parts.add(p.toString());
}
return fromNameParts(parts, 0);
}
public static TypeName fromNameParts(List<String> parts, int array) {
String type = parts.get(parts.size() - 1);
parts = parts.subList(0, parts.size() - 1);
String innerType = null;
int pos = type.indexOf('$');
if (pos != -1) {
innerType = type.substring(pos + 1);
type = type.substring(0, pos);
}
return new TypeName(Joiner.on('.').join(parts), type, innerType, array);
}
public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
throws AndrolibException {
String origInternal = internal;
int array = 0;
boolean isArray = false;
do {
if (internal.isEmpty()) {
throw new AndrolibException("Invalid internal name: "
+ origInternal);
}
isArray = internal.charAt(0) == '[';
if (isArray) {
array++;
internal = internal.substring(1);
}
} while (isArray);
int length = array + 1;
String type;
switch (internal.charAt(0)) {
case 'B':
type = "byte";
break;
case 'C':
type = "char";
break;
case 'D':
type = "double";
break;
case 'F':
type = "float";
break;
case 'I':
type = "int";
break;
case 'J':
type = "long";
break;
case 'S':
type = "short";
break;
case 'Z':
type = "boolean";
break;
case 'V':
type = "void";
break;
case 'L':
int pos = internal.indexOf(';');
if (pos == -1) {
throw new AndrolibException("Invalid internal name: "
+ origInternal);
}
return new Duo<>(fromNameParts(Arrays.asList(internal.substring(1, pos).split("/")), array), length + pos);
default:
throw new AndrolibException("Invalid internal name: "
+ origInternal);
}
return new Duo<>(new TypeName(null, type, null, array), length);
}
private Boolean mIsFileOwner;
}