diff options
Diffstat (limited to 'lint/libs/lint_api')
4 files changed, 86 insertions, 8 deletions
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java index 93e311e..4cca31c 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintClient.java @@ -33,6 +33,7 @@ import com.android.tools.lint.detector.api.Project; import com.android.tools.lint.detector.api.Severity; import com.google.common.annotations.Beta; import com.google.common.collect.Maps; +import com.google.common.io.Files; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -177,6 +178,22 @@ public abstract class LintClient { public abstract String readFile(@NonNull File file); /** + * Reads the given binary file and returns the content as a byte array. + * By default this method will read the bytes from the file directly, + * but this can be customized by a client if for example I/O could be + * held in memory and not flushed to disk yet. + * + * @param file the file to read + * @return the bytes in the file, never null + * @throws IOException if the file does not exist, or if the file cannot be + * read for some reason + */ + @NonNull + public byte[] readBytes(@NonNull File file) throws IOException { + return Files.toByteArray(file); + } + + /** * Returns the list of source folders for Java source files * * @param project the project to look up Java source file locations for diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java index 270ffa4..d91b155 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java @@ -55,7 +55,6 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.google.common.io.ByteStreams; import com.google.common.io.Closeables; -import com.google.common.io.Files; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -1023,7 +1022,7 @@ public class LintDriver { String path = file.getPath(); if (file.isFile() && path.endsWith(DOT_CLASS)) { try { - byte[] bytes = Files.toByteArray(file); + byte[] bytes = mClient.readBytes(file); if (bytes != null) { for (File dir : classFolders) { if (path.startsWith(dir.getPath())) { @@ -1068,6 +1067,8 @@ public class LintDriver { if (classDetectors != null && classDetectors.size() > 0 && entries.size() > 0) { AsmVisitor visitor = new AsmVisitor(mClient, classDetectors); + String sourceContents = null; + String sourceName = ""; mOuterClasses = new ArrayDeque<ClassNode>(); for (ClassEntry entry : entries) { ClassReader reader; @@ -1097,9 +1098,31 @@ public class LintDriver { continue; } + if (sourceContents != null) { + // Attempt to reuse the source buffer if initialized + // This means making sure that the source files + // foo/bar/MyClass and foo/bar/MyClass$Bar + // and foo/bar/MyClass$3 and foo/bar/MyClass$3$1 have the same prefix. + String newName = classNode.name; + int newRootLength = newName.indexOf('$'); + if (newRootLength == -1) { + newRootLength = newName.length(); + } + int oldRootLength = sourceName.indexOf('$'); + if (oldRootLength == -1) { + oldRootLength = sourceName.length(); + } + if (newRootLength != oldRootLength || + !sourceName.regionMatches(0, newName, 0, newRootLength)) { + sourceContents = null; + } + } + ClassContext context = new ClassContext(this, project, main, entry.file, entry.jarFile, entry.binDir, entry.bytes, - classNode, scope == Scope.JAVA_LIBRARIES /*fromLibrary*/); + classNode, scope == Scope.JAVA_LIBRARIES /*fromLibrary*/, + sourceContents); + try { visitor.runClassDetectors(context); } catch (Exception e) { @@ -1109,6 +1132,9 @@ public class LintDriver { if (mCanceled) { return; } + + sourceContents = context.getSourceContents(false/*read*/); + sourceName = classNode.name; } mOuterClasses = null; @@ -1229,7 +1255,7 @@ public class LintDriver { for (File file : classFiles) { try { - byte[] bytes = Files.toByteArray(file); + byte[] bytes = mClient.readBytes(file); if (bytes != null) { entries.add(new ClassEntry(file, null /* jarFile*/, binDir, bytes)); } @@ -1568,6 +1594,12 @@ public class LintDriver { @Override @NonNull + public byte[] readBytes(@NonNull File file) throws IOException { + return mDelegate.readBytes(file); + } + + @Override + @NonNull public List<File> getJavaSourceFolders(@NonNull Project project) { return mDelegate.getJavaSourceFolders(project); } diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java index 4aa21d4..5f8cac9 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java @@ -82,6 +82,8 @@ public class ClassContext extends Context { * @param classNode the bytecode object model * @param fromLibrary whether this class is from a library rather than part * of this project + * @param sourceContents initial contents of the Java source, if known, or + * null */ public ClassContext( @NonNull LintDriver driver, @@ -92,13 +94,15 @@ public class ClassContext extends Context { @NonNull File binDir, @NonNull byte[] bytes, @NonNull ClassNode classNode, - boolean fromLibrary) { + boolean fromLibrary, + @Nullable String sourceContents) { super(driver, project, main, file); mJarFile = jarFile; mBinDir = binDir; mBytes = bytes; mClassNode = classNode; mFromLibrary = fromLibrary; + mSourceContents = sourceContents; } /** @@ -217,6 +221,28 @@ public class ClassContext extends Context { } /** + * Returns the contents of the source file for this class file, if found. If + * {@code read} is false, do not read the source contents if it has not + * already been read. (This is primarily intended for the lint + * infrastructure; most client code would call {@link #getSourceContents()} + * .) + * + * @param read whether to read the source contents if it has not already + * been initialized + * @return the source contents, which will never be null if {@code read} is + * true, or null if {@code read} is false and the source contents + * hasn't already been read. + */ + @Nullable + public String getSourceContents(boolean read) { + if (read) { + return getSourceContents(); + } else { + return mSourceContents; + } + } + + /** * Returns a location for the given source line number in this class file's * source file, if available. * diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java index 9bfb7cc..ce64fe3 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java @@ -27,9 +27,9 @@ import com.android.annotations.Nullable; import com.android.resources.FolderTypeRelationship; import com.android.resources.ResourceFolderType; import com.android.resources.ResourceType; +import com.android.tools.lint.client.api.LintClient; import com.android.util.PositionXmlParser; import com.google.common.annotations.Beta; -import com.google.common.io.Files; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; @@ -394,13 +394,16 @@ public class LintUtils { * same as {@code Files.toString(file, Charsets.UTF8}, but if there's a UTF byte order mark * (for UTF8, UTF_16 or UTF_16LE), use that instead. * + * @param client the client to use for I/O operations * @param file the file to read from * @return the string * @throws IOException if the file cannot be read properly */ @NonNull - public static String getEncodedString(@NonNull File file) throws IOException { - byte[] bytes = Files.toByteArray(file); + public static String getEncodedString( + @NonNull LintClient client, + @NonNull File file) throws IOException { + byte[] bytes = client.readBytes(file); if (endsWith(file.getName(), DOT_XML)) { return PositionXmlParser.getXmlString(bytes); } |