mirror of
https://github.com/revanced/smali.git
synced 2025-05-08 18:34:32 +02:00
Revamp how classpath loading works
This commit is contained in:
parent
8a5a6e3fc5
commit
e474301e60
@ -36,7 +36,6 @@ import org.jf.dexlib2.iface.DexFile;
|
|||||||
import org.jf.util.ClassFileNameHandler;
|
import org.jf.util.ClassFileNameHandler;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
@ -155,75 +154,4 @@ public class Baksmali {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static List<String> getDefaultBootClassPath(int apiLevel) {
|
|
||||||
if (apiLevel < 9) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar");
|
|
||||||
} else if (apiLevel < 12) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/core-junit.jar");
|
|
||||||
} else if (apiLevel < 14) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/apache-xml.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/core-junit.jar");
|
|
||||||
} else if (apiLevel < 16) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/core-junit.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/apache-xml.jar",
|
|
||||||
"/system/framework/filterfw.jar");
|
|
||||||
} else if (apiLevel < 21) {
|
|
||||||
// this is correct as of api 17/4.2.2
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/core-junit.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/telephony-common.jar",
|
|
||||||
"/system/framework/mms-common.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/apache-xml.jar");
|
|
||||||
} else { // api >= 21
|
|
||||||
// TODO: verify, add new ones?
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core-libart.jar",
|
|
||||||
"/system/framework/conscrypt.jar",
|
|
||||||
"/system/framework/okhttp.jar",
|
|
||||||
"/system/framework/core-junit.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/telephony-common.jar",
|
|
||||||
"/system/framework/voip-common.jar",
|
|
||||||
"/system/framework/ims-common.jar",
|
|
||||||
"/system/framework/mms-common.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/apache-xml.jar");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -35,12 +35,12 @@ import com.beust.jcommander.JCommander;
|
|||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.Parameters;
|
import com.beust.jcommander.Parameters;
|
||||||
import com.beust.jcommander.validators.PositiveInteger;
|
import com.beust.jcommander.validators.PositiveInteger;
|
||||||
import com.google.common.collect.Iterables;
|
import org.jf.dexlib2.DexFileFactory;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import org.jf.dexlib2.analysis.ClassPath;
|
import org.jf.dexlib2.analysis.ClassPath;
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
import org.jf.dexlib2.dexbacked.OatFile;
|
||||||
import org.jf.dexlib2.iface.DexFile;
|
import org.jf.dexlib2.iface.DexFile;
|
||||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||||
import org.jf.util.StringWrapper;
|
import org.jf.util.StringWrapper;
|
||||||
@ -48,6 +48,7 @@ import org.jf.util.jcommander.CommaColonParameterSplitter;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ public class DisassembleCommand extends DexInputCommand {
|
|||||||
"bootclasspath files that baksmali would otherwise perform. This is analogous to Android's " +
|
"bootclasspath files that baksmali would otherwise perform. This is analogous to Android's " +
|
||||||
"BOOTCLASSPATH environment variable.",
|
"BOOTCLASSPATH environment variable.",
|
||||||
splitter = CommaColonParameterSplitter.class)
|
splitter = CommaColonParameterSplitter.class)
|
||||||
private List<String> bootClassPath = new ArrayList<String>();
|
private List<String> bootClassPath = null;
|
||||||
|
|
||||||
@Parameter(names = {"-c", "--classpath"},
|
@Parameter(names = {"-c", "--classpath"},
|
||||||
description = "A comma/colon separated list of additional jar/oat files to include in the classpath " +
|
description = "A comma/colon separated list of additional jar/oat files to include in the classpath " +
|
||||||
@ -85,7 +86,8 @@ public class DisassembleCommand extends DexInputCommand {
|
|||||||
private List<String> classPath = Lists.newArrayList();
|
private List<String> classPath = Lists.newArrayList();
|
||||||
|
|
||||||
@Parameter(names = {"-d", "--classpath-dir"},
|
@Parameter(names = {"-d", "--classpath-dir"},
|
||||||
description = "baksmali will search these directories in order for any classpath entries.")
|
description = "A directory to search for classpath files. This option can be used multiple times to " +
|
||||||
|
"specify multiple directories to search. They will be searched in the order they are provided.")
|
||||||
private List<String> classPathDirectories = Lists.newArrayList(".");
|
private List<String> classPathDirectories = Lists.newArrayList(".");
|
||||||
|
|
||||||
@Parameter(names = {"--code-offsets"},
|
@Parameter(names = {"--code-offsets"},
|
||||||
@ -170,16 +172,39 @@ public class DisassembleCommand extends DexInputCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputList.size() > 1) {
|
String input = inputList.get(0);
|
||||||
System.err.println("Too many files specified");
|
File dexFileFile = new File(input);
|
||||||
jc.usage(jc.getParsedCommand());
|
String dexFileEntry = null;
|
||||||
return;
|
if (!dexFileFile.exists()) {
|
||||||
|
String filename = dexFileFile.getName();
|
||||||
|
int colonIndex = filename.indexOf(':');
|
||||||
|
|
||||||
|
if (colonIndex >= 0) {
|
||||||
|
dexFileFile = new File(dexFileFile.getParent(), filename.substring(0, colonIndex));
|
||||||
|
dexFileEntry = filename.substring(colonIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
String input = inputList.get(0);
|
if (!dexFileFile.exists()) {
|
||||||
DexBackedDexFile dexFile = loadDexFile(input, apiLevel, experimentalOpcodes);
|
System.err.println("Can't find the file " + input);
|
||||||
if (dexFile == null) {
|
System.exit(1);
|
||||||
return;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Read in and parse the dex file
|
||||||
|
DexBackedDexFile dexFile = null;
|
||||||
|
try {
|
||||||
|
dexFile = DexFileFactory.loadDexFile(dexFileFile, dexFileEntry, apiLevel, experimentalOpcodes);
|
||||||
|
} catch (DexFileFactory.MultipleDexFilesException ex) {
|
||||||
|
System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
|
||||||
|
"disassemble with the -e option", dexFileFile.getName()));
|
||||||
|
System.err.println("Valid entries include:");
|
||||||
|
|
||||||
|
for (OatFile.OatDexFile oatDexFile : ex.oatFile.getDexFiles()) {
|
||||||
|
System.err.println(oatDexFile.filename);
|
||||||
|
}
|
||||||
|
System.exit(1);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showDeodexWarning() && dexFile.hasOdexOpcodes()) {
|
if (showDeodexWarning() && dexFile.hasOdexOpcodes()) {
|
||||||
@ -188,14 +213,6 @@ public class DisassembleCommand extends DexInputCommand {
|
|||||||
"re-assemble the results unless you deodex it. See \"baksmali help deodex\"");
|
"re-assemble the results unless you deodex it. See \"baksmali help deodex\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsClassPath() && bootClassPath.isEmpty()) {
|
|
||||||
if (dexFile instanceof DexBackedOdexFile) {
|
|
||||||
bootClassPath = ((DexBackedOdexFile)dexFile).getDependencies();
|
|
||||||
} else {
|
|
||||||
bootClassPath = Baksmali.getDefaultBootClassPath(apiLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File outputDirectoryFile = new File(outputDir);
|
File outputDirectoryFile = new File(outputDir);
|
||||||
if (!outputDirectoryFile.exists()) {
|
if (!outputDirectoryFile.exists()) {
|
||||||
if (!outputDirectoryFile.mkdirs()) {
|
if (!outputDirectoryFile.mkdirs()) {
|
||||||
@ -227,13 +244,13 @@ public class DisassembleCommand extends DexInputCommand {
|
|||||||
|
|
||||||
if (needsClassPath()) {
|
if (needsClassPath()) {
|
||||||
try {
|
try {
|
||||||
options.classPath = ClassPath.fromClassPath(classPathDirectories,
|
options.classPath = ClassPath.loadClassPath(classPathDirectories,
|
||||||
Iterables.concat(bootClassPath, classPath), dexFile, apiLevel,
|
bootClassPath, classPath, dexFile, apiLevel, shouldCheckPackagePrivateAccess(),
|
||||||
shouldCheckPackagePrivateAccess(), experimentalOpcodes);
|
experimentalOpcodes);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
System.err.println("\n\nError occurred while loading class path files. Aborting.");
|
System.err.println("\n\nError occurred while loading class path files. Aborting.");
|
||||||
ex.printStackTrace(System.err);
|
ex.printStackTrace(System.err);
|
||||||
return null;
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ package org.jf.baksmali;
|
|||||||
import com.beust.jcommander.JCommander;
|
import com.beust.jcommander.JCommander;
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.Parameters;
|
import com.beust.jcommander.Parameters;
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.jf.dexlib2.analysis.ClassPath;
|
import org.jf.dexlib2.analysis.ClassPath;
|
||||||
import org.jf.dexlib2.analysis.ClassProto;
|
import org.jf.dexlib2.analysis.ClassProto;
|
||||||
@ -142,9 +141,8 @@ public class ListFieldOffsetsCommand extends DexInputCommand {
|
|||||||
options.apiLevel = apiLevel;
|
options.apiLevel = apiLevel;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
options.classPath = ClassPath.fromClassPath(classPathDirectories,
|
options.classPath = ClassPath.loadClassPath(classPathDirectories,
|
||||||
Iterables.concat(bootClassPath, classPath), dexFile, apiLevel,
|
bootClassPath, classPath, dexFile, apiLevel, checkPackagePrivateAccess, experimentalOpcodes);
|
||||||
checkPackagePrivateAccess, experimentalOpcodes);
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
System.err.println("Error occurred while loading class path files.");
|
System.err.println("Error occurred while loading class path files.");
|
||||||
ex.printStackTrace(System.err);
|
ex.printStackTrace(System.err);
|
||||||
|
@ -34,7 +34,6 @@ package org.jf.baksmali;
|
|||||||
import com.beust.jcommander.JCommander;
|
import com.beust.jcommander.JCommander;
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.Parameters;
|
import com.beust.jcommander.Parameters;
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.jf.dexlib2.analysis.ClassPath;
|
import org.jf.dexlib2.analysis.ClassPath;
|
||||||
import org.jf.dexlib2.analysis.ClassProto;
|
import org.jf.dexlib2.analysis.ClassProto;
|
||||||
@ -164,9 +163,8 @@ public class ListVtablesCommand extends DexInputCommand {
|
|||||||
options.apiLevel = apiLevel;
|
options.apiLevel = apiLevel;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
options.classPath = ClassPath.fromClassPath(classPathDirectories,
|
options.classPath = ClassPath.loadClassPath(classPathDirectories, bootClassPath, classPath, dexFile,
|
||||||
Iterables.concat(bootClassPath, classPath), dexFile, apiLevel, checkPackagePrivateAccess,
|
apiLevel, checkPackagePrivateAccess, experimentalOpcodes);
|
||||||
experimentalOpcodes);
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
System.err.println("Error occurred while loading class path files.");
|
System.err.println("Error occurred while loading class path files.");
|
||||||
ex.printStackTrace(System.err);
|
ex.printStackTrace(System.err);
|
||||||
|
@ -36,28 +36,29 @@ import com.google.common.base.Suppliers;
|
|||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.*;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.io.Files;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.primitives.Ints;
|
||||||
import org.jf.dexlib2.DexFileFactory;
|
import org.jf.dexlib2.DexFileFactory;
|
||||||
import org.jf.dexlib2.DexFileFactory.DexFileNotFound;
|
|
||||||
import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
|
import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
|
||||||
import org.jf.dexlib2.Opcodes;
|
import org.jf.dexlib2.Opcodes;
|
||||||
import org.jf.dexlib2.analysis.reflection.ReflectionClassDef;
|
import org.jf.dexlib2.analysis.reflection.ReflectionClassDef;
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||||
|
import org.jf.dexlib2.dexbacked.OatFile;
|
||||||
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
|
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
|
||||||
import org.jf.dexlib2.iface.ClassDef;
|
import org.jf.dexlib2.iface.ClassDef;
|
||||||
import org.jf.dexlib2.iface.DexFile;
|
import org.jf.dexlib2.iface.DexFile;
|
||||||
import org.jf.dexlib2.immutable.ImmutableDexFile;
|
import org.jf.dexlib2.immutable.ImmutableDexFile;
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
|
import org.jf.util.PathUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class ClassPath {
|
public class ClassPath {
|
||||||
@Nonnull private final TypeProto unknownClass;
|
@Nonnull private final TypeProto unknownClass;
|
||||||
@ -164,117 +165,181 @@ public class ClassPath {
|
|||||||
return checkPackagePrivateAccess;
|
return checkPackagePrivateAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ClassPath given a set of user inputs
|
||||||
|
*
|
||||||
|
* This performs all the magic in finding the right defaults based on the values provided and what type of dex
|
||||||
|
* file we have. E.g. choosing the right default bootclasspath if needed, actually locating the files on the
|
||||||
|
* filesystem, etc.
|
||||||
|
*
|
||||||
|
* This is meant to be as forgiving as possible and to generally "do the right thing" based on the given inputs.
|
||||||
|
*
|
||||||
|
* @param classPathDirs A list of directories to search for class path entries in. Be sure to include "." to search
|
||||||
|
* the current working directory, if appropriate.
|
||||||
|
* @param bootClassPathEntries A list of boot class path entries to load. This can be just the bare filenames,
|
||||||
|
* relative paths, absolute paths based on the local directory structure, absolute paths
|
||||||
|
* based on the device directory structure, etc. It can contain paths to
|
||||||
|
* jar/dex/oat/odex files, or just bare filenames with no extension, etc.
|
||||||
|
* If non-null and blank, then no entries will be loaded other than dexFile
|
||||||
|
* If null, it will attempt to use the correct defaults based on the inputs.
|
||||||
|
* @param extraClassPathEntries Additional class path entries. The same sorts of naming mechanisms as for
|
||||||
|
* bootClassPathEntries are allowed
|
||||||
|
* @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by
|
||||||
|
* default
|
||||||
|
* @param dexFile The dex file that will be analyzed. It can be a dex, odex or oat file.
|
||||||
|
* @param api The api level of the device that these dex files come from.
|
||||||
|
* @param experimental Whether to allow experimental opcodes
|
||||||
|
*
|
||||||
|
* @return A ClassPath object
|
||||||
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
public static ClassPath loadClassPath(@Nonnull Iterable<String> classPathDirs,
|
||||||
int api, boolean experimental) {
|
@Nullable Iterable<String> bootClassPathEntries,
|
||||||
return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17, experimental);
|
@Nonnull Iterable<String> extraClassPathEntries, @Nonnull DexFile dexFile,
|
||||||
|
int api, boolean experimental, boolean checkPackagePrivateAccess)
|
||||||
|
throws IOException {
|
||||||
|
List<ClassProvider> classProviders = Lists.newArrayList();
|
||||||
|
if (bootClassPathEntries == null) {
|
||||||
|
bootClassPathEntries = getDefaultDeviceBootClassPath(dexFile, api);
|
||||||
|
}
|
||||||
|
if (extraClassPathEntries == null) {
|
||||||
|
extraClassPathEntries = ImmutableList.of();
|
||||||
|
}
|
||||||
|
for (String entry: Iterables.concat(bootClassPathEntries, extraClassPathEntries)) {
|
||||||
|
List<File> files = Lists.newArrayList();
|
||||||
|
|
||||||
|
for (String extension: new String[] { null, ".apk", ".jar", ".odex", ".oat", ".dex" }) {
|
||||||
|
String searchEntry = entry;
|
||||||
|
if (Files.getFileExtension(entry).equals(extension)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (extension != null) {
|
||||||
|
searchEntry = Files.getNameWithoutExtension(entry) + extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
for (String dir: classPathDirs) {
|
||||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
files.addAll(findFiles(new File(dir), new File(searchEntry).getName(), 100));
|
||||||
int api, boolean checkPackagePrivateAccess, boolean experimental) {
|
}
|
||||||
List<ClassProvider> providers = Lists.newArrayList();
|
if (files.size() > 0) {
|
||||||
|
|
||||||
int oatVersion = NOT_ART;
|
|
||||||
|
|
||||||
for (String classPathEntry: classPath) {
|
|
||||||
List<? extends DexFile> classPathDexFiles =
|
|
||||||
loadClassPathEntry(classPathDirs, classPathEntry, api, experimental);
|
|
||||||
if (oatVersion == NOT_ART) {
|
|
||||||
for (DexFile classPathDexFile: classPathDexFiles) {
|
|
||||||
if (classPathDexFile instanceof OatDexFile) {
|
|
||||||
oatVersion = ((OatDexFile)classPathDexFile).getOatVersion();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for (DexFile classPathDexFile: classPathDexFiles) {
|
if (files.size() == 0) {
|
||||||
providers.add(new DexClassProvider(classPathDexFile));
|
throw new FileNotFoundException(String.format("Classpath entry %s could not be found", entry));
|
||||||
}
|
|
||||||
}
|
|
||||||
providers.add(new DexClassProvider(dexFile));
|
|
||||||
return new ClassPath(providers, checkPackagePrivateAccess, oatVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
File bestMatch = Collections.max(files, new ClassPathEntryComparator(entry));
|
||||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
|
||||||
int api, boolean checkPackagePrivateAccess, boolean experimental,
|
|
||||||
int oatVersion) {
|
|
||||||
List<ClassProvider> providers = Lists.newArrayList();
|
|
||||||
|
|
||||||
for (String classPathEntry: classPath) {
|
|
||||||
List<? extends DexFile> classPathDexFiles =
|
|
||||||
loadClassPathEntry(classPathDirs, classPathEntry, api, experimental);
|
|
||||||
for (DexFile classPathDexFile: classPathDexFiles) {
|
|
||||||
providers.add(new DexClassProvider(classPathDexFile));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
providers.add(new DexClassProvider(dexFile));
|
|
||||||
return new ClassPath(providers, checkPackagePrivateAccess, oatVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private static List<? extends DexFile> loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
|
|
||||||
@Nonnull String bootClassPathEntry, int api,
|
|
||||||
boolean experimental) {
|
|
||||||
File rawEntry = new File(bootClassPathEntry);
|
|
||||||
// strip off the path - we only care about the filename
|
|
||||||
String entryName = rawEntry.getName();
|
|
||||||
|
|
||||||
// if it's a dalvik-cache entry, grab the name of the jar/apk
|
|
||||||
if (entryName.endsWith("@classes.dex")) {
|
|
||||||
Matcher m = dalvikCacheOdexPattern.matcher(entryName);
|
|
||||||
|
|
||||||
if (!m.find()) {
|
|
||||||
throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", bootClassPathEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
entryName = m.group(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int extIndex = entryName.lastIndexOf(".");
|
|
||||||
|
|
||||||
String baseEntryName;
|
|
||||||
if (extIndex == -1) {
|
|
||||||
baseEntryName = entryName;
|
|
||||||
} else {
|
|
||||||
baseEntryName = entryName.substring(0, extIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String classPathDir: classPathDirs) {
|
|
||||||
String[] extensions;
|
|
||||||
|
|
||||||
if (entryName.endsWith(".oat")) {
|
|
||||||
extensions = new String[] { ".oat" };
|
|
||||||
} else {
|
|
||||||
extensions = new String[] { "", ".odex", ".jar", ".apk", ".zip" };
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String ext: extensions) {
|
|
||||||
File file = new File(classPathDir, baseEntryName + ext);
|
|
||||||
|
|
||||||
if (file.exists() && file.isFile()) {
|
|
||||||
if (!file.canRead()) {
|
|
||||||
System.err.println(String.format(
|
|
||||||
"warning: cannot open %s for reading. Will continue looking.", file.getPath()));
|
|
||||||
} else {
|
|
||||||
try {
|
try {
|
||||||
return ImmutableList.of(DexFileFactory.loadDexFile(file, api, experimental));
|
DexFile entryDexFile = DexFileFactory.loadDexFile(bestMatch, api, experimental);
|
||||||
} catch (DexFileNotFound ex) {
|
classProviders.add(new DexClassProvider(entryDexFile));
|
||||||
// ignore and continue
|
|
||||||
} catch (MultipleDexFilesException ex) {
|
} catch (MultipleDexFilesException ex) {
|
||||||
return ex.oatFile.getDexFiles();
|
for (DexFile entryDexFile: ex.oatFile.getDexFiles()) {
|
||||||
} catch (Exception ex) {
|
classProviders.add(new DexClassProvider(entryDexFile));
|
||||||
throw ExceptionWithContext.withContext(ex,
|
|
||||||
"Error while reading boot class path entry \"%s\"", bootClassPathEntry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int oatVersion = -1;
|
||||||
|
if (dexFile instanceof OatDexFile) {
|
||||||
|
oatVersion = ((OatDexFile)dexFile).getOatVersion();
|
||||||
|
}
|
||||||
|
classProviders.add(new DexClassProvider(dexFile));
|
||||||
|
|
||||||
|
return new ClassPath(classProviders, checkPackagePrivateAccess, oatVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassPathEntryComparator implements Comparator<File> {
|
||||||
|
@Nonnull private List<String> reversePathComponents;
|
||||||
|
|
||||||
|
public ClassPathEntryComparator(@Nonnull String entry) {
|
||||||
|
// TODO: will PathUtil.getPathComponents work for unix-style paths while on windows?
|
||||||
|
this.reversePathComponents = Lists.reverse(PathUtil.getPathComponents(new File(entry)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int compare(File file1, File file2) {
|
||||||
|
int comparison = Ints.compare(countMatchingComponents(file1), countMatchingComponents(file2));
|
||||||
|
if (comparison != 0) {
|
||||||
|
// the path that matches the entry being searched for wins
|
||||||
|
return comparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
comparison = Ints.compare(PathUtil.getPathComponents(file1).size(),
|
||||||
|
PathUtil.getPathComponents(file2).size());
|
||||||
|
if (comparison != 0) {
|
||||||
|
// the path "higher up" (with fewer directories) wins
|
||||||
|
return comparison * -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise.. just return the first one alphabetically.
|
||||||
|
return file1.compareTo(file2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countMatchingComponents(File file) {
|
||||||
|
for (int i=0; i<reversePathComponents.size(); i++) {
|
||||||
|
if (file == null) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
if (!file.getName().equals(reversePathComponents.get(i))) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
file = file.getParentFile();
|
||||||
|
}
|
||||||
|
return reversePathComponents.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ye olde recursive file search.
|
||||||
|
*
|
||||||
|
* Searches for all files (not directories!) named "name".
|
||||||
|
*
|
||||||
|
* It attempts to detect filesystem loops via File.getCanonicalPath, and will not recurse path maxDepth directories.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private static List<File> findFiles(@Nonnull File dir, @Nonnull String name, int maxDepth) throws IOException {
|
||||||
|
List<File> files = Lists.newArrayList();
|
||||||
|
Set<String> visitedPaths = Sets.newHashSet();
|
||||||
|
|
||||||
|
if (!dir.exists()) {
|
||||||
|
throw new IllegalArgumentException(String.format("Directory %s does not exist", dir.getPath()));
|
||||||
|
}
|
||||||
|
if (!dir.isDirectory()) {
|
||||||
|
throw new IllegalArgumentException(String.format("%s is not a directory", dir.getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
findFiles(files, visitedPaths, dir, name, maxDepth);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void findFiles(@Nonnull List<File> result, @Nonnull Set<String> visitedPaths, @Nonnull File dir,
|
||||||
|
@Nonnull String name, int maxDepth) throws IOException {
|
||||||
|
if (maxDepth < 0 || !visitedPaths.add(dir.getCanonicalPath())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] children = dir.listFiles();
|
||||||
|
if (children == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (File child: children) {
|
||||||
|
if (child.isDirectory()) {
|
||||||
|
findFiles(result, visitedPaths, child, name, maxDepth-1);
|
||||||
|
} else {
|
||||||
|
if (name.equals(child.getName())) {
|
||||||
|
try {
|
||||||
|
DexFileFactory.loadDexFile(child, 15);
|
||||||
|
} catch (ExceptionWithContext ex) {
|
||||||
|
if (!(ex instanceof MultipleDexFilesException)) {
|
||||||
|
// Don't add it to the results if it can't be loaded
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
|
private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
|
||||||
@ -288,4 +353,133 @@ public class ClassPath {
|
|||||||
public OdexedFieldInstructionMapper getFieldInstructionMapper() {
|
public OdexedFieldInstructionMapper getFieldInstructionMapper() {
|
||||||
return fieldInstructionMapperSupplier.get();
|
return fieldInstructionMapperSupplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default boot class path for the given api. This is boot class path that is used for "stock"
|
||||||
|
* (i.e nexus) images for the given api level, but may not be correct for devices with heavily modified firmware.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private static List<String> getDefaultDeviceBootClassPath(DexFile dexFile, int apiLevel) {
|
||||||
|
if (dexFile instanceof OatFile.OatDexFile) {
|
||||||
|
if (((OatFile.OatDexFile) dexFile).getOatVersion() >= 74) {
|
||||||
|
return ((OatFile.OatDexFile) dexFile).getOatFile().getBootClassPath();
|
||||||
|
} else {
|
||||||
|
return Lists.newArrayList("boot.oat");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dexFile instanceof DexBackedOdexFile) {
|
||||||
|
return ((DexBackedOdexFile)dexFile).getDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiLevel <= 8) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar");
|
||||||
|
} else if (apiLevel <= 11) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/core-junit.jar");
|
||||||
|
} else if (apiLevel <= 13) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/apache-xml.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/core-junit.jar");
|
||||||
|
} else if (apiLevel <= 15) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/apache-xml.jar",
|
||||||
|
"/system/framework/filterfw.jar");
|
||||||
|
} else if (apiLevel <= 17) {
|
||||||
|
// this is correct as of api 17/4.2.2
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/telephony-common.jar",
|
||||||
|
"/system/framework/mms-common.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/apache-xml.jar");
|
||||||
|
} else if (apiLevel <= 18) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/telephony-common.jar",
|
||||||
|
"/system/framework/voip-common.jar",
|
||||||
|
"/system/framework/mms-common.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/apache-xml.jar");
|
||||||
|
} else if (apiLevel <= 19) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/conscrypt.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/framework2.jar",
|
||||||
|
"/system/framework/telephony-common.jar",
|
||||||
|
"/system/framework/voip-common.jar",
|
||||||
|
"/system/framework/mms-common.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/apache-xml.jar",
|
||||||
|
"/system/framework/webviewchromium.jar");
|
||||||
|
} else if (apiLevel <= 22) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core-libart.jar",
|
||||||
|
"/system/framework/conscrypt.jar",
|
||||||
|
"/system/framework/okhttp.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/telephony-common.jar",
|
||||||
|
"/system/framework/voip-common.jar",
|
||||||
|
"/system/framework/ims-common.jar",
|
||||||
|
"/system/framework/mms-common.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/apache-xml.jar");
|
||||||
|
} else /*if (apiLevel <= 23)*/ {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core-libart.jar",
|
||||||
|
"/system/framework/conscrypt.jar",
|
||||||
|
"/system/framework/okhttp.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/telephony-common.jar",
|
||||||
|
"/system/framework/voip-common.jar",
|
||||||
|
"/system/framework/ims-common.jar",
|
||||||
|
"/system/framework/apache-xml.jar",
|
||||||
|
"/system/framework/org.apache.http.legacy.boot.jar");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,11 +51,12 @@ import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35mi;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CustomMethodInlineTableTest {
|
public class CustomMethodInlineTableTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCustomMethodInlineTable_Virtual() {
|
public void testCustomMethodInlineTable_Virtual() throws IOException {
|
||||||
List<ImmutableInstruction> instructions = Lists.newArrayList(
|
List<ImmutableInstruction> instructions = Lists.newArrayList(
|
||||||
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
|
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
|
||||||
new ImmutableInstruction10x(Opcode.RETURN_VOID));
|
new ImmutableInstruction10x(Opcode.RETURN_VOID));
|
||||||
@ -69,8 +70,9 @@ public class CustomMethodInlineTableTest {
|
|||||||
|
|
||||||
DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
|
DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
|
||||||
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
ClassPath classPath = ClassPath.loadClassPath(ImmutableList.<String>of(),
|
||||||
15, false);
|
ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile, 15, false, false);
|
||||||
|
|
||||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
||||||
|
|
||||||
@ -82,7 +84,7 @@ public class CustomMethodInlineTableTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomMethodInlineTable_Static() {
|
public void testCustomMethodInlineTable_Static() throws IOException {
|
||||||
List<ImmutableInstruction> instructions = Lists.newArrayList(
|
List<ImmutableInstruction> instructions = Lists.newArrayList(
|
||||||
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
|
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
|
||||||
new ImmutableInstruction10x(Opcode.RETURN_VOID));
|
new ImmutableInstruction10x(Opcode.RETURN_VOID));
|
||||||
@ -96,8 +98,8 @@ public class CustomMethodInlineTableTest {
|
|||||||
|
|
||||||
DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
|
DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
|
||||||
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
ClassPath classPath = ClassPath.loadClassPath(ImmutableList.<String>of(),
|
||||||
15, false);
|
ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile, 15, false, false);
|
||||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
||||||
|
|
||||||
@ -109,7 +111,7 @@ public class CustomMethodInlineTableTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomMethodInlineTable_Direct() {
|
public void testCustomMethodInlineTable_Direct() throws IOException {
|
||||||
List<ImmutableInstruction> instructions = Lists.newArrayList(
|
List<ImmutableInstruction> instructions = Lists.newArrayList(
|
||||||
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
|
new ImmutableInstruction35mi(Opcode.EXECUTE_INLINE, 1, 0, 0, 0, 0, 0, 0),
|
||||||
new ImmutableInstruction10x(Opcode.RETURN_VOID));
|
new ImmutableInstruction10x(Opcode.RETURN_VOID));
|
||||||
@ -123,8 +125,8 @@ public class CustomMethodInlineTableTest {
|
|||||||
|
|
||||||
DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
|
DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
|
||||||
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
ClassPath classPath = ClassPath.loadClassPath(ImmutableList.<String>of(),
|
||||||
15, false);
|
ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile, 15, false, false);
|
||||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
||||||
|
|
||||||
|
@ -28,9 +28,12 @@
|
|||||||
|
|
||||||
package org.jf.util;
|
package org.jf.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class PathUtil {
|
public class PathUtil {
|
||||||
private PathUtil() {
|
private PathUtil() {
|
||||||
@ -55,8 +58,8 @@ public class PathUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String getRelativeFileInternal(File canonicalBaseFile, File canonicalFileToRelativize) {
|
static String getRelativeFileInternal(File canonicalBaseFile, File canonicalFileToRelativize) {
|
||||||
ArrayList<String> basePath = getPathComponents(canonicalBaseFile);
|
List<String> basePath = getPathComponents(canonicalBaseFile);
|
||||||
ArrayList<String> pathToRelativize = getPathComponents(canonicalFileToRelativize);
|
List<String> pathToRelativize = getPathComponents(canonicalFileToRelativize);
|
||||||
|
|
||||||
//if the roots aren't the same (i.e. different drives on a windows machine), we can't construct a relative
|
//if the roots aren't the same (i.e. different drives on a windows machine), we can't construct a relative
|
||||||
//path from one to the other, so just return the canonical file
|
//path from one to the other, so just return the canonical file
|
||||||
@ -105,21 +108,21 @@ public class PathUtil {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<String> getPathComponents(File file) {
|
public static List<String> getPathComponents(File file) {
|
||||||
ArrayList<String> path = new ArrayList<String>();
|
ArrayList<String> path = new ArrayList<String>();
|
||||||
|
|
||||||
while (file != null) {
|
while (file != null) {
|
||||||
File parentFile = file.getParentFile();
|
File parentFile = file.getParentFile();
|
||||||
|
|
||||||
if (parentFile == null) {
|
if (parentFile == null) {
|
||||||
path.add(0, file.getPath());
|
path.add(file.getPath());
|
||||||
} else {
|
} else {
|
||||||
path.add(0, file.getName());
|
path.add(file.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
file = parentFile;
|
file = parentFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return Lists.reverse(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user