summaryrefslogtreecommitdiffstats
path: root/dx
diff options
context:
space:
mode:
authorYohann Roussel <yroussel@google.com>2014-11-18 19:15:02 +0100
committerYohann Roussel <yroussel@google.com>2014-12-01 10:55:22 +0100
commit8e88bab04f03edbbc74fb61b31a847c22106bfab (patch)
tree9fd09a90baebf9988ee2db615456de9b1f8668dc /dx
parent9747950064e4711c15baf0986102320a2512097a (diff)
downloadandroid_dalvik-8e88bab04f03edbbc74fb61b31a847c22106bfab.tar.gz
android_dalvik-8e88bab04f03edbbc74fb61b31a847c22106bfab.tar.bz2
android_dalvik-8e88bab04f03edbbc74fb61b31a847c22106bfab.zip
Keep annotated classes in main dex list
Only classes annotated with a runtime visible annotation are to be kept. See https://code.google.com/p/android/issues/detail?id=78144 Bug: 18385117 (cherry picked from commit 893795fc95fdd77d398ebb77a0fe336c45b596cf) Change-Id: I46ceef4aab5119ba4515a07e8f17577e15931dbd
Diffstat (limited to 'dx')
-rwxr-xr-xdx/etc/mainDexClasses2
-rwxr-xr-xdx/etc/mainDexClasses.bat4
-rw-r--r--dx/src/com/android/dx/cf/iface/ClassFile.java2
-rw-r--r--dx/src/com/android/dx/cf/iface/HasAttribute.java32
-rw-r--r--dx/src/com/android/dx/cf/iface/Member.java2
-rw-r--r--dx/src/com/android/multidex/ArchivePathElement.java53
-rw-r--r--dx/src/com/android/multidex/ClassPathElement.java2
-rw-r--r--dx/src/com/android/multidex/ClassReferenceListBuilder.java211
-rw-r--r--dx/src/com/android/multidex/FolderPathElement.java18
-rw-r--r--dx/src/com/android/multidex/MainDexListBuilder.java167
-rw-r--r--dx/src/com/android/multidex/Path.java119
11 files changed, 410 insertions, 202 deletions
diff --git a/dx/etc/mainDexClasses b/dx/etc/mainDexClasses
index 034d47eaa..28c0f0c89 100755
--- a/dx/etc/mainDexClasses
+++ b/dx/etc/mainDexClasses
@@ -155,4 +155,4 @@ ${proguard} -injars ${@} -dontwarn -forceprocessing -outjars ${tmpOut} \
-libraryjars "${shrinkedAndroidJar}" -dontoptimize -dontobfuscate -dontpreverify \
-include "${baserules}" 1>/dev/null || exit 10
-java -cp "$jarpath" com.android.multidex.ClassReferenceListBuilder "${tmpOut}" ${@} || exit 11
+java -cp "$jarpath" com.android.multidex.MainDexListBuilder "${tmpOut}" ${@} || exit 11
diff --git a/dx/etc/mainDexClasses.bat b/dx/etc/mainDexClasses.bat
index 00b60e8de..f6a4b56ca 100755
--- a/dx/etc/mainDexClasses.bat
+++ b/dx/etc/mainDexClasses.bat
@@ -96,10 +96,10 @@ set "exitStatus=0"
call "%proguard%" -injars %params% -dontwarn -forceprocessing -outjars "%tmpJar%" -libraryjars "%shrinkedAndroidJar%" -dontoptimize -dontobfuscate -dontpreverify -include "%baserules%" 1>nul
if DEFINED output goto redirect
-call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.ClassReferenceListBuilder "%tmpJar%" "%params%"
+call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.MainDexListBuilder "%tmpJar%" "%params%"
goto afterClassReferenceListBuilder
:redirect
-call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.ClassReferenceListBuilder "%tmpJar%" "%params%" 1>"%output%"
+call "%java_exe%" -Djava.ext.dirs="%frameworkdir%" com.android.multidex.MainDexListBuilder "%tmpJar%" "%params%" 1>"%output%"
:afterClassReferenceListBuilder
del %tmpJar%
diff --git a/dx/src/com/android/dx/cf/iface/ClassFile.java b/dx/src/com/android/dx/cf/iface/ClassFile.java
index cb5237a50..d6c9ed03e 100644
--- a/dx/src/com/android/dx/cf/iface/ClassFile.java
+++ b/dx/src/com/android/dx/cf/iface/ClassFile.java
@@ -28,7 +28,7 @@ import com.android.dx.rop.type.TypeList;
* <p><b>Note:</b> The fields referred to in this documentation are of the
* {@code ClassFile} structure defined in vmspec-2 sec4.1.
*/
-public interface ClassFile {
+public interface ClassFile extends HasAttribute {
/**
* Gets the field {@code magic}.
*
diff --git a/dx/src/com/android/dx/cf/iface/HasAttribute.java b/dx/src/com/android/dx/cf/iface/HasAttribute.java
new file mode 100644
index 000000000..9f3e48db2
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/HasAttribute.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.dx.cf.iface;
+
+/**
+ * An element that can have {@link Attribute}
+ */
+public interface HasAttribute {
+
+ /**
+ * Get the element {@code attributes} (along with
+ * {@code attributes_count}).
+ *
+ * @return {@code non-null;} the attributes list
+ */
+ public AttributeList getAttributes();
+
+}
diff --git a/dx/src/com/android/dx/cf/iface/Member.java b/dx/src/com/android/dx/cf/iface/Member.java
index b346de447..1097d1906 100644
--- a/dx/src/com/android/dx/cf/iface/Member.java
+++ b/dx/src/com/android/dx/cf/iface/Member.java
@@ -23,7 +23,7 @@ import com.android.dx.rop.cst.CstType;
/**
* Interface representing members of class files (that is, fields and methods).
*/
-public interface Member {
+public interface Member extends HasAttribute {
/**
* Get the defining class.
*
diff --git a/dx/src/com/android/multidex/ArchivePathElement.java b/dx/src/com/android/multidex/ArchivePathElement.java
index e76993bd9..05788d1bb 100644
--- a/dx/src/com/android/multidex/ArchivePathElement.java
+++ b/dx/src/com/android/multidex/ArchivePathElement.java
@@ -19,6 +19,9 @@ package com.android.multidex;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -27,7 +30,10 @@ import java.util.zip.ZipFile;
*/
class ArchivePathElement implements ClassPathElement {
- private ZipFile archive;
+ static class DirectoryEntryException extends IOException {
+ }
+
+ private final ZipFile archive;
public ArchivePathElement(ZipFile archive) {
this.archive = archive;
@@ -37,7 +43,9 @@ class ArchivePathElement implements ClassPathElement {
public InputStream open(String path) throws IOException {
ZipEntry entry = archive.getEntry(path);
if (entry == null) {
- throw new FileNotFoundException(path);
+ throw new FileNotFoundException("File \"" + path + "\" not found");
+ } else if (entry.isDirectory()) {
+ throw new DirectoryEntryException();
} else {
return archive.getInputStream(entry);
}
@@ -48,4 +56,45 @@ class ArchivePathElement implements ClassPathElement {
archive.close();
}
+ @Override
+ public Iterable<String> list() {
+ return new Iterable<String>() {
+
+ @Override
+ public Iterator<String> iterator() {
+ return new Iterator<String>() {
+ Enumeration<? extends ZipEntry> delegate = archive.entries();
+ ZipEntry next = null;
+
+ @Override
+ public boolean hasNext() {
+ while (next == null && delegate.hasMoreElements()) {
+ next = delegate.nextElement();
+ if (next.isDirectory()) {
+ next = null;
+ }
+ }
+ return next != null;
+ }
+
+ @Override
+ public String next() {
+ if (hasNext()) {
+ String name = next.getName();
+ next = null;
+ return name;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
}
diff --git a/dx/src/com/android/multidex/ClassPathElement.java b/dx/src/com/android/multidex/ClassPathElement.java
index 6c60721a4..aee81cdcb 100644
--- a/dx/src/com/android/multidex/ClassPathElement.java
+++ b/dx/src/com/android/multidex/ClassPathElement.java
@@ -36,4 +36,6 @@ interface ClassPathElement {
void close() throws IOException;
+ Iterable<String> list();
+
}
diff --git a/dx/src/com/android/multidex/ClassReferenceListBuilder.java b/dx/src/com/android/multidex/ClassReferenceListBuilder.java
index 7a9b11d3d..0434cad98 100644
--- a/dx/src/com/android/multidex/ClassReferenceListBuilder.java
+++ b/dx/src/com/android/multidex/ClassReferenceListBuilder.java
@@ -17,114 +17,41 @@
package com.android.multidex;
import com.android.dx.cf.direct.DirectClassFile;
-import com.android.dx.cf.direct.StdAttributeFactory;
import com.android.dx.rop.cst.Constant;
import com.android.dx.rop.cst.ConstantPool;
import com.android.dx.rop.cst.CstType;
import com.android.dx.rop.type.Type;
import com.android.dx.rop.type.TypeList;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
-import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
import java.util.zip.ZipFile;
/**
- * This is a command line tool used by mainDexClasses script to find direct class references to
- * other classes. First argument of the command line is an archive, each class file contained in
- * this archive is used to identify a class whose references are to be searched, those class files
- * are not opened by this tool only their names matter. Other arguments must be zip files or
- * directories, they constitute in a classpath in with the classes named by the first argument
- * will be searched. Each searched class must be found. On each of this classes are searched for
- * their dependencies to other classes. Finally the tools prints on standard output a list of class
- * files names suitable as content of the file argument --main-dex-list of dx.
+ * Tool to find direct class references to other classes.
*/
public class ClassReferenceListBuilder {
-
private static final String CLASS_EXTENSION = ".class";
- private static final int STATUS_ERROR = 1;
-
- private static final String EOL = System.getProperty("line.separator");
-
- private static String USAGE_MESSAGE =
- "Usage:" + EOL + EOL +
- "Short version: Don't use this." + EOL + EOL +
- "Slightly longer version: This tool is used by mainDexClasses script to find direct"
- + EOL +
- "references of some classes." + EOL;
-
private Path path;
- private Set<String> toKeep = new HashSet<String>();
-
- /**
- *
- * @param inputPath list of path to input jars or folders. Path elements must be separated by
- * the system path separator: ':' on Unix, ';' on Windows.
- */
- public ClassReferenceListBuilder(String inputPath) throws IOException {
- this(new Path(inputPath));
- }
+ private Set<String> classNames = new HashSet<String>();
- private ClassReferenceListBuilder(Path path) {
+ public ClassReferenceListBuilder(Path path) {
this.path = path;
}
+ /**
+ * Kept for compatibility with the gradle integration, this method just forwards to
+ * {@link MainDexListBuilder#main(String[])}.
+ * @deprecated use {@link MainDexListBuilder#main(String[])} instead.
+ */
+ @Deprecated
public static void main(String[] args) {
-
- if (args.length != 2) {
- printUsage();
- System.exit(STATUS_ERROR);
- }
-
- ZipFile jarOfRoots;
- try {
- jarOfRoots = new ZipFile(args[0]);
- } catch (IOException e) {
- System.err.println("\"" + args[0] + "\" can not be read as a zip archive. ("
- + e.getMessage() + ")");
- System.exit(STATUS_ERROR);
- return;
- }
-
- Path path = null;
- try {
- path = new Path(args[1]);
-
- ClassReferenceListBuilder builder = new ClassReferenceListBuilder(path);
- builder.addRoots(jarOfRoots);
-
- printList(builder.toKeep);
- } catch (IOException e) {
- System.err.println("A fatal error occured: " + e.getMessage());
- System.exit(STATUS_ERROR);
- return;
- } finally {
- try {
- jarOfRoots.close();
- } catch (IOException e) {
- // ignore
- }
- if (path != null) {
- for (ClassPathElement element : path.elements) {
- try {
- element.close();
- } catch (IOException e) {
- // keep going, lets do our best.
- }
- }
- }
- }
+ MainDexListBuilder.main(args);
}
/**
@@ -139,7 +66,7 @@ public class ClassReferenceListBuilder {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(CLASS_EXTENSION)) {
- toKeep.add(name.substring(0, name.length() - CLASS_EXTENSION.length()));
+ classNames.add(name.substring(0, name.length() - CLASS_EXTENSION.length()));
}
}
@@ -162,41 +89,8 @@ public class ClassReferenceListBuilder {
}
}
- /**
- * Returns a list of classes to keep. This can be passed to dx as a file with --main-dex-list.
- */
- public Set<String> getMainDexList() {
- Set<String> resultSet = new HashSet<String>(toKeep.size());
- for (String classDescriptor : toKeep) {
- resultSet.add(classDescriptor + CLASS_EXTENSION);
- }
-
- return resultSet;
- }
-
- private static void printUsage() {
- System.err.print(USAGE_MESSAGE);
- }
-
- private static ClassPathElement getClassPathElement(File file)
- throws ZipException, IOException {
- if (file.isDirectory()) {
- return new FolderPathElement(file);
- } else if (file.isFile()) {
- return new ArchivePathElement(new ZipFile(file));
- } else if (file.exists()) {
- throw new IOException(file.getAbsolutePath() +
- " is not a directory neither a zip file");
- } else {
- throw new FileNotFoundException(file.getAbsolutePath());
- }
- }
-
- private static void printList(Set<String> toKeep) {
- for (String classDescriptor : toKeep) {
- System.out.print(classDescriptor);
- System.out.println(CLASS_EXTENSION);
- }
+ Set<String> getClassNames() {
+ return classNames;
}
private void addDependencies(ConstantPool pool) {
@@ -220,14 +114,13 @@ public class ClassReferenceListBuilder {
}
private void addClassWithHierachy(String classBinaryName) {
- if (toKeep.contains(classBinaryName)) {
+ if (classNames.contains(classBinaryName)) {
return;
}
- String fileName = classBinaryName + CLASS_EXTENSION;
try {
- DirectClassFile classFile = path.getClass(fileName);
- toKeep.add(classBinaryName);
+ DirectClassFile classFile = path.getClass(classBinaryName + CLASS_EXTENSION);
+ classNames.add(classBinaryName);
CstType superClass = classFile.getSuperclass();
if (superClass != null) {
addClassWithHierachy(superClass.getClassType().getClassName());
@@ -243,76 +136,4 @@ public class ClassReferenceListBuilder {
}
}
- private static class Path {
- private List<ClassPathElement> elements = new ArrayList<ClassPathElement>();
- private String definition;
- private ByteArrayOutputStream baos = new ByteArrayOutputStream(40 * 1024);
- private byte[] readBuffer = new byte[20 * 1024];
-
- private Path(String definition) throws IOException {
- this.definition = definition;
- for (String filePath : definition.split(Pattern.quote(File.pathSeparator))) {
- try {
- addElement(getClassPathElement(new File(filePath)));
- } catch (IOException e) {
- throw new IOException("\"" + filePath + "\" can not be used as a classpath"
- + " element. ("
- + e.getMessage() + ")", e);
- }
- }
- }
-
- private static byte[] readStream(InputStream in, ByteArrayOutputStream baos, byte[] readBuffer)
- throws IOException {
- try {
- for (;;) {
- int amt = in.read(readBuffer);
- if (amt < 0) {
- break;
- }
-
- baos.write(readBuffer, 0, amt);
- }
- } finally {
- in.close();
- }
- return baos.toByteArray();
- }
-
- @Override
- public String toString() {
- return definition;
- }
-
- private void addElement(ClassPathElement element) {
- assert element != null;
- elements.add(element);
- }
-
- private DirectClassFile getClass(String path) throws FileNotFoundException {
- DirectClassFile classFile = null;
- for (ClassPathElement element : elements) {
- try {
- InputStream in = element.open(path);
- try {
- byte[] bytes = readStream(in, baos, readBuffer);
- baos.reset();
- classFile = new DirectClassFile(bytes, path, false);
- classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
- break;
- } finally {
- in.close();
- }
- } catch (IOException e) {
- // search next element
- }
- }
- if (classFile == null) {
- throw new FileNotFoundException(path);
- }
- return classFile;
- }
- }
-
-
}
diff --git a/dx/src/com/android/multidex/FolderPathElement.java b/dx/src/com/android/multidex/FolderPathElement.java
index 224254730..97fb11f17 100644
--- a/dx/src/com/android/multidex/FolderPathElement.java
+++ b/dx/src/com/android/multidex/FolderPathElement.java
@@ -20,6 +20,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
+import java.util.ArrayList;
/**
* A folder element.
@@ -42,4 +43,21 @@ class FolderPathElement implements ClassPathElement {
public void close() {
}
+ @Override
+ public Iterable<String> list() {
+ ArrayList<String> result = new ArrayList<String>();
+ collect(baseFolder, "", result);
+ return result;
+ }
+
+ private void collect(File folder, String prefix, ArrayList<String> result) {
+ for (File file : folder.listFiles()) {
+ if (file.isDirectory()) {
+ collect(file, prefix + SEPARATOR_CHAR + file.getName(), result);
+ } else {
+ result.add(prefix + SEPARATOR_CHAR + file.getName());
+ }
+ }
+ }
+
}
diff --git a/dx/src/com/android/multidex/MainDexListBuilder.java b/dx/src/com/android/multidex/MainDexListBuilder.java
new file mode 100644
index 000000000..c9e1a186c
--- /dev/null
+++ b/dx/src/com/android/multidex/MainDexListBuilder.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 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.multidex;
+
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.FieldList;
+import com.android.dx.cf.iface.HasAttribute;
+import com.android.dx.cf.iface.MethodList;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipFile;
+
+/**
+ * This is a command line tool used by mainDexClasses script to build a main dex classes list. First
+ * argument of the command line is an archive, each class file contained in this archive is used to
+ * identify a class that can be used during secondary dex installation, those class files
+ * are not opened by this tool only their names matter. Other arguments must be zip files or
+ * directories, they constitute in a classpath in with the classes named by the first argument
+ * will be searched. Each searched class must be found. On each of this classes are searched for
+ * their dependencies to other classes. The tool also browses for classes annotated by runtime
+ * visible annotations and adds them to the list/ Finally the tools prints on standard output a list
+ * of class files names suitable as content of the file argument --main-dex-list of dx.
+ */
+public class MainDexListBuilder {
+ private static final String CLASS_EXTENSION = ".class";
+
+ private static final int STATUS_ERROR = 1;
+
+ private static final String EOL = System.getProperty("line.separator");
+
+ private static String USAGE_MESSAGE =
+ "Usage:" + EOL + EOL +
+ "Short version: Don't use this." + EOL + EOL +
+ "Slightly longer version: This tool is used by mainDexClasses script to build" + EOL +
+ "the main dex list." + EOL;
+
+ private Set<String> filesToKeep = new HashSet<String>();
+
+ public static void main(String[] args) {
+
+ if (args.length != 2) {
+ printUsage();
+ System.exit(STATUS_ERROR);
+ }
+
+ try {
+
+ MainDexListBuilder builder = new MainDexListBuilder(args[0], args[1]);
+ Set<String> toKeep = builder.getMainDexList();
+ printList(toKeep);
+ } catch (IOException e) {
+ System.err.println("A fatal error occured: " + e.getMessage());
+ System.exit(STATUS_ERROR);
+ return;
+ }
+ }
+
+ public MainDexListBuilder(String rootJar, String pathString) throws IOException {
+ ZipFile jarOfRoots = null;
+ Path path = null;
+ try {
+ try {
+ jarOfRoots = new ZipFile(rootJar);
+ } catch (IOException e) {
+ throw new IOException("\"" + rootJar + "\" can not be read as a zip archive. ("
+ + e.getMessage() + ")", e);
+ }
+ path = new Path(pathString);
+
+ ClassReferenceListBuilder mainListBuilder = new ClassReferenceListBuilder(path);
+ mainListBuilder.addRoots(jarOfRoots);
+ for (String className : mainListBuilder.getClassNames()) {
+ filesToKeep.add(className + CLASS_EXTENSION);
+ }
+ keepAnnotated(path);
+ } finally {
+ try {
+ jarOfRoots.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ if (path != null) {
+ for (ClassPathElement element : path.elements) {
+ try {
+ element.close();
+ } catch (IOException e) {
+ // keep going, lets do our best.
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a list of classes to keep. This can be passed to dx as a file with --main-dex-list.
+ */
+ public Set<String> getMainDexList() {
+ return filesToKeep;
+ }
+
+ private static void printUsage() {
+ System.err.print(USAGE_MESSAGE);
+ }
+
+ private static void printList(Set<String> fileNames) {
+ for (String fileName : fileNames) {
+ System.out.println(fileName);
+ }
+ }
+
+ /**
+ * Keep classes annotated with runtime annotations.
+ */
+ private void keepAnnotated(Path path) throws FileNotFoundException {
+ for (ClassPathElement element : path.getElements()) {
+ forClazz:
+ for (String name : element.list()) {
+ if (name.endsWith(CLASS_EXTENSION)) {
+ DirectClassFile clazz = path.getClass(name);
+ if (hasRuntimeVisibleAnnotation(clazz)) {
+ filesToKeep.add(name);
+ } else {
+ MethodList methods = clazz.getMethods();
+ for (int i = 0; i<methods.size(); i++) {
+ if (hasRuntimeVisibleAnnotation(methods.get(i))) {
+ filesToKeep.add(name);
+ continue forClazz;
+ }
+ }
+ FieldList fields = clazz.getFields();
+ for (int i = 0; i<fields.size(); i++) {
+ if (hasRuntimeVisibleAnnotation(fields.get(i))) {
+ filesToKeep.add(name);
+ continue forClazz;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean hasRuntimeVisibleAnnotation(HasAttribute element) {
+ Attribute att = element.getAttributes().findFirst(
+ AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+ return (att != null && ((AttRuntimeVisibleAnnotations)att).getAnnotations().size()>0);
+ }
+}
diff --git a/dx/src/com/android/multidex/Path.java b/dx/src/com/android/multidex/Path.java
new file mode 100644
index 000000000..155b40f98
--- /dev/null
+++ b/dx/src/com/android/multidex/Path.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 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.multidex;
+
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+class Path {
+
+ static ClassPathElement getClassPathElement(File file)
+ throws ZipException, IOException {
+ if (file.isDirectory()) {
+ return new FolderPathElement(file);
+ } else if (file.isFile()) {
+ return new ArchivePathElement(new ZipFile(file));
+ } else if (file.exists()) {
+ throw new IOException("\"" + file.getPath() +
+ "\" is not a directory neither a zip file");
+ } else {
+ throw new FileNotFoundException("File \"" + file.getPath() + "\" not found");
+ }
+ }
+
+ List<ClassPathElement> elements = new ArrayList<ClassPathElement>();
+ private final String definition;
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream(40 * 1024);
+ private final byte[] readBuffer = new byte[20 * 1024];
+
+ Path(String definition) throws IOException {
+ this.definition = definition;
+ for (String filePath : definition.split(Pattern.quote(File.pathSeparator))) {
+ try {
+ addElement(getClassPathElement(new File(filePath)));
+ } catch (IOException e) {
+ throw new IOException("Wrong classpath: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ private static byte[] readStream(InputStream in, ByteArrayOutputStream baos, byte[] readBuffer)
+ throws IOException {
+ try {
+ for (;;) {
+ int amt = in.read(readBuffer);
+ if (amt < 0) {
+ break;
+ }
+
+ baos.write(readBuffer, 0, amt);
+ }
+ } finally {
+ in.close();
+ }
+ return baos.toByteArray();
+ }
+
+ @Override
+ public String toString() {
+ return definition;
+ }
+
+ Iterable<ClassPathElement> getElements() {
+ return elements;
+ }
+
+ private void addElement(ClassPathElement element) {
+ assert element != null;
+ elements.add(element);
+ }
+
+ synchronized DirectClassFile getClass(String path) throws FileNotFoundException {
+ DirectClassFile classFile = null;
+ for (ClassPathElement element : elements) {
+ try {
+ InputStream in = element.open(path);
+ try {
+ byte[] bytes = readStream(in, baos, readBuffer);
+ baos.reset();
+ classFile = new DirectClassFile(bytes, path, false);
+ classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ break;
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ // search next element
+ }
+ }
+ if (classFile == null) {
+ throw new FileNotFoundException("File \"" + path + "\" not found");
+ }
+ return classFile;
+ }
+} \ No newline at end of file