From 2cf03dc15c40b92634ff606694af5a6e9aa4db09 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 22 May 2013 11:05:31 -0700 Subject: Adding libart support to libcore Change-Id: I86febf08eacf42bb4b2f06dbd51c5b2d5b25c9fb --- JavaLibrary.mk | 6 + .../java/dalvik/system/BaseDexClassLoader.java | 139 ++ libart/src/main/java/dalvik/system/DexFile.java | 318 ++++ .../src/main/java/dalvik/system/DexPathList.java | 451 ++++++ libart/src/main/java/java/lang/Class.java | 1630 ++++++++++++++++++++ libart/src/main/java/java/lang/ClassLoader.java | 856 ++++++++++ libart/src/main/java/java/lang/Daemons.java | 340 ++++ libart/src/main/java/java/lang/DexCache.java | 39 + libart/src/main/java/java/lang/Object.java | 452 ++++++ libart/src/main/java/java/lang/Thread.java | 1254 +++++++++++++++ libart/src/main/java/java/lang/ThreadGroup.java | 726 +++++++++ libart/src/main/java/java/lang/VMClassLoader.java | 78 + .../java/java/lang/reflect/AbstractMethod.java | 362 +++++ .../java/java/lang/reflect/AccessibleObject.java | 93 ++ .../main/java/java/lang/reflect/Constructor.java | 376 +++++ libart/src/main/java/java/lang/reflect/Field.java | 804 ++++++++++ libart/src/main/java/java/lang/reflect/Method.java | 540 +++++++ libart/src/main/java/java/lang/reflect/Proxy.java | 373 +++++ .../java/libcore/reflect/AnnotationAccess.java | 794 ++++++++++ .../java/libcore/reflect/AnnotationFactory.java | 314 ++++ .../libcore/reflect/GenericSignatureParser.java | 496 ++++++ .../main/java/libcore/reflect/ListOfVariables.java | 35 + .../java/libcore/reflect/TypeVariableImpl.java | 136 ++ libart/src/main/java/sun/misc/Unsafe.java | 338 ++++ 24 files changed, 10950 insertions(+) create mode 100644 libart/src/main/java/dalvik/system/BaseDexClassLoader.java create mode 100644 libart/src/main/java/dalvik/system/DexFile.java create mode 100644 libart/src/main/java/dalvik/system/DexPathList.java create mode 100644 libart/src/main/java/java/lang/Class.java create mode 100644 libart/src/main/java/java/lang/ClassLoader.java create mode 100644 libart/src/main/java/java/lang/Daemons.java create mode 100644 libart/src/main/java/java/lang/DexCache.java create mode 100644 libart/src/main/java/java/lang/Object.java create mode 100644 libart/src/main/java/java/lang/Thread.java create mode 100644 libart/src/main/java/java/lang/ThreadGroup.java create mode 100644 libart/src/main/java/java/lang/VMClassLoader.java create mode 100644 libart/src/main/java/java/lang/reflect/AbstractMethod.java create mode 100644 libart/src/main/java/java/lang/reflect/AccessibleObject.java create mode 100644 libart/src/main/java/java/lang/reflect/Constructor.java create mode 100644 libart/src/main/java/java/lang/reflect/Field.java create mode 100644 libart/src/main/java/java/lang/reflect/Method.java create mode 100644 libart/src/main/java/java/lang/reflect/Proxy.java create mode 100644 libart/src/main/java/libcore/reflect/AnnotationAccess.java create mode 100644 libart/src/main/java/libcore/reflect/AnnotationFactory.java create mode 100644 libart/src/main/java/libcore/reflect/GenericSignatureParser.java create mode 100644 libart/src/main/java/libcore/reflect/ListOfVariables.java create mode 100644 libart/src/main/java/libcore/reflect/TypeVariableImpl.java create mode 100644 libart/src/main/java/sun/misc/Unsafe.java diff --git a/JavaLibrary.mk b/JavaLibrary.mk index a28e609..0e57401 100644 --- a/JavaLibrary.mk +++ b/JavaLibrary.mk @@ -51,7 +51,13 @@ endef # The Java files and their associated resources. core_src_files := $(call all-main-java-files-under,dalvik dex dom json luni support xml) + +ifeq ($(WITH_ART),false) core_src_files += $(call all-main-java-files-under,libdvm) +else +core_src_files += $(call all-main-java-files-under,libart) +endif + core_resource_dirs := $(call all-core-resource-dirs,main) test_resource_dirs := $(call all-core-resource-dirs,test) diff --git a/libart/src/main/java/dalvik/system/BaseDexClassLoader.java b/libart/src/main/java/dalvik/system/BaseDexClassLoader.java new file mode 100644 index 0000000..6a1a493 --- /dev/null +++ b/libart/src/main/java/dalvik/system/BaseDexClassLoader.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2011 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 dalvik.system; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * Base class for common functionality between various dex-based + * {@link ClassLoader} implementations. + */ +public class BaseDexClassLoader extends ClassLoader { + private final DexPathList pathList; + + /** + * Constructs an instance. + * + * @param dexPath the list of jar/apk files containing classes and + * resources, delimited by {@code File.pathSeparator}, which + * defaults to {@code ":"} on Android + * @param optimizedDirectory directory where optimized dex files + * should be written; may be {@code null} + * @param libraryPath the list of directories containing native + * libraries, delimited by {@code File.pathSeparator}; may be + * {@code null} + * @param parent the parent class loader + */ + public BaseDexClassLoader(String dexPath, File optimizedDirectory, + String libraryPath, ClassLoader parent) { + super(parent); + this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + List suppressedExceptions = new ArrayList(); + Class c = pathList.findClass(name, suppressedExceptions); + if (c == null) { + ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList); + for (Throwable t : suppressedExceptions) { + cnfe.addSuppressed(t); + } + throw cnfe; + } + return c; + } + + @Override + protected URL findResource(String name) { + return pathList.findResource(name); + } + + @Override + protected Enumeration findResources(String name) { + return pathList.findResources(name); + } + + @Override + public String findLibrary(String name) { + return pathList.findLibrary(name); + } + + /** + * Returns package information for the given package. + * Unfortunately, instances of this class don't really have this + * information, and as a non-secure {@code ClassLoader}, it isn't + * even required to, according to the spec. Yet, we want to + * provide it, in order to make all those hopeful callers of + * {@code myClass.getPackage().getName()} happy. Thus we construct + * a {@code Package} object the first time it is being requested + * and fill most of the fields with dummy values. The {@code + * Package} object is then put into the {@code ClassLoader}'s + * package cache, so we see the same one next time. We don't + * create {@code Package} objects for {@code null} arguments or + * for the default package. + * + *

There is a limited chance that we end up with multiple + * {@code Package} objects representing the same package: It can + * happen when when a package is scattered across different JAR + * files which were loaded by different {@code ClassLoader} + * instances. This is rather unlikely, and given that this whole + * thing is more or less a workaround, probably not worth the + * effort to address. + * + * @param name the name of the class + * @return the package information for the class, or {@code null} + * if there is no package information available for it + */ + @Override + protected synchronized Package getPackage(String name) { + if (name != null && !name.isEmpty()) { + Package pack = super.getPackage(name); + + if (pack == null) { + pack = definePackage(name, "Unknown", "0.0", "Unknown", + "Unknown", "0.0", "Unknown", null); + } + + return pack; + } + + return null; + } + + /** + * @hide + */ + public String getLdLibraryPath() { + StringBuilder result = new StringBuilder(); + for (File directory : pathList.getNativeLibraryDirectories()) { + if (result.length() > 0) { + result.append(':'); + } + result.append(directory); + } + return result.toString(); + } + + @Override public String toString() { + return getClass().getName() + "[" + pathList + "]"; + } +} diff --git a/libart/src/main/java/dalvik/system/DexFile.java b/libart/src/main/java/dalvik/system/DexFile.java new file mode 100644 index 0000000..06b834a --- /dev/null +++ b/libart/src/main/java/dalvik/system/DexFile.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2007 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 dalvik.system; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import libcore.io.ErrnoException; +import libcore.io.Libcore; +import libcore.io.StructStat; + +/** + * Manipulates DEX files. The class is similar in principle to + * {@link java.util.zip.ZipFile}. It is used primarily by class loaders. + *

+ * Note we don't directly open and read the DEX file here. They're memory-mapped + * read-only by the VM. + */ +public final class DexFile { + private int mCookie; + private final String mFileName; + private final CloseGuard guard = CloseGuard.get(); + + /** + * Opens a DEX file from a given File object. This will usually be a ZIP/JAR + * file with a "classes.dex" inside. + * + * The VM will generate the name of the corresponding file in + * /data/dalvik-cache and open it, possibly creating or updating + * it first if system permissions allow. Don't pass in the name of + * a file in /data/dalvik-cache, as the named file is expected to be + * in its original (pre-dexopt) state. + * + * @param file + * the File object referencing the actual DEX file + * + * @throws IOException + * if an I/O error occurs, such as the file not being found or + * access rights missing for opening it + */ + public DexFile(File file) throws IOException { + this(file.getPath()); + } + + /** + * Opens a DEX file from a given filename. This will usually be a ZIP/JAR + * file with a "classes.dex" inside. + * + * The VM will generate the name of the corresponding file in + * /data/dalvik-cache and open it, possibly creating or updating + * it first if system permissions allow. Don't pass in the name of + * a file in /data/dalvik-cache, as the named file is expected to be + * in its original (pre-dexopt) state. + * + * @param fileName + * the filename of the DEX file + * + * @throws IOException + * if an I/O error occurs, such as the file not being found or + * access rights missing for opening it + */ + public DexFile(String fileName) throws IOException { + mCookie = openDexFile(fileName, null, 0); + mFileName = fileName; + guard.open("close"); + //System.out.println("DEX FILE cookie is " + mCookie); + } + + /** + * Opens a DEX file from a given filename, using a specified file + * to hold the optimized data. + * + * @param sourceName + * Jar or APK file with "classes.dex". + * @param outputName + * File that will hold the optimized form of the DEX data. + * @param flags + * Enable optional features. + */ + private DexFile(String sourceName, String outputName, int flags) throws IOException { + if (outputName != null) { + try { + String parent = new File(outputName).getParent(); + if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) { + throw new IllegalArgumentException("Optimized data directory " + parent + + " is not owned by the current user. Shared storage cannot protect" + + " your application from code injection attacks."); + } + } catch (ErrnoException ignored) { + // assume we'll fail with a more contextual error later + } + } + + mCookie = openDexFile(sourceName, outputName, flags); + mFileName = sourceName; + guard.open("close"); + //System.out.println("DEX FILE cookie is " + mCookie); + } + + /** + * Open a DEX file, specifying the file in which the optimized DEX + * data should be written. If the optimized form exists and appears + * to be current, it will be used; if not, the VM will attempt to + * regenerate it. + * + * This is intended for use by applications that wish to download + * and execute DEX files outside the usual application installation + * mechanism. This function should not be called directly by an + * application; instead, use a class loader such as + * dalvik.system.DexClassLoader. + * + * @param sourcePathName + * Jar or APK file with "classes.dex". (May expand this to include + * "raw DEX" in the future.) + * @param outputPathName + * File that will hold the optimized form of the DEX data. + * @param flags + * Enable optional features. (Currently none defined.) + * @return + * A new or previously-opened DexFile. + * @throws IOException + * If unable to open the source or output file. + */ + static public DexFile loadDex(String sourcePathName, String outputPathName, + int flags) throws IOException { + + /* + * TODO: we may want to cache previously-opened DexFile objects. + * The cache would be synchronized with close(). This would help + * us avoid mapping the same DEX more than once when an app + * decided to open it multiple times. In practice this may not + * be a real issue. + */ + return new DexFile(sourcePathName, outputPathName, flags); + } + + /** + * Gets the name of the (already opened) DEX file. + * + * @return the file name + */ + public String getName() { + return mFileName; + } + + /** + * Closes the DEX file. + *

+ * This may not be able to release any resources. If classes from this + * DEX file are still resident, the DEX file can't be unmapped. + * + * @throws IOException + * if an I/O error occurs during closing the file, which + * normally should not happen + */ + public void close() throws IOException { + if (mCookie != 0) { + guard.close(); + closeDexFile(mCookie); + mCookie = 0; + } + } + + /** + * Loads a class. Returns the class on success, or a {@code null} reference + * on failure. + *

+ * If you are not calling this from a class loader, this is most likely not + * going to do what you want. Use {@link Class#forName(String)} instead. + *

+ * The method does not throw {@link ClassNotFoundException} if the class + * isn't found because it isn't reasonable to throw exceptions wildly every + * time a class is not found in the first DEX file we look at. + * + * @param name + * the class name, which should look like "java/lang/String" + * + * @param loader + * the class loader that tries to load the class (in most cases + * the caller of the method + * + * @return the {@link Class} object representing the class, or {@code null} + * if the class cannot be loaded + */ + public Class loadClass(String name, ClassLoader loader) { + String slashName = name.replace('.', '/'); + return loadClassBinaryName(slashName, loader, null); + } + + /** + * See {@link #loadClass(String, ClassLoader)}. + * + * This takes a "binary" class name to better match ClassLoader semantics. + * + * @hide + */ + public Class loadClassBinaryName(String name, ClassLoader loader, List suppressed) { + return defineClass(name, loader, mCookie, suppressed); + } + + private static Class defineClass(String name, ClassLoader loader, int cookie, + List suppressed) { + Class result = null; + try { + result = defineClassNative(name, loader, cookie); + } catch (NoClassDefFoundError e) { + if (suppressed != null) { + suppressed.add(e); + } + } catch (ClassNotFoundException e) { + if (suppressed != null) { + suppressed.add(e); + } + } + return result; + } + + private static native Class defineClassNative(String name, ClassLoader loader, int cookie) + throws ClassNotFoundException, NoClassDefFoundError; + + /** + * Enumerate the names of the classes in this DEX file. + * + * @return an enumeration of names of classes contained in the DEX file, in + * the usual internal form (like "java/lang/String"). + */ + public Enumeration entries() { + return new DFEnum(this); + } + + /* + * Helper class. + */ + private class DFEnum implements Enumeration { + private int mIndex; + private String[] mNameList; + + DFEnum(DexFile df) { + mIndex = 0; + mNameList = getClassNameList(mCookie); + } + + public boolean hasMoreElements() { + return (mIndex < mNameList.length); + } + + public String nextElement() { + return mNameList[mIndex++]; + } + } + + /* return a String array with class names */ + native private static String[] getClassNameList(int cookie); + + /** + * Called when the class is finalized. Makes sure the DEX file is closed. + * + * @throws IOException + * if an I/O error occurs during closing the file, which + * normally should not happen + */ + @Override protected void finalize() throws Throwable { + try { + if (guard != null) { + guard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); + } + } + + /* + * Open a DEX file. The value returned is a magic VM cookie. On + * failure, an IOException is thrown. + */ + native private static int openDexFile(String sourceName, String outputName, + int flags) throws IOException; + + /* + * Close DEX file. + */ + native private static void closeDexFile(int cookie); + + /** + * Returns true if the VM believes that the apk/jar file is out of date + * and should be passed through "dexopt" again. + * + * @param fileName the absolute path to the apk/jar file to examine. + * @return true if dexopt should be called on the file, false otherwise. + * @throws java.io.FileNotFoundException if fileName is not readable, + * not a file, or not present. + * @throws java.io.IOException if fileName is not a valid apk/jar file or + * if problems occur while parsing it. + * @throws java.lang.NullPointerException if fileName is null. + * @throws dalvik.system.StaleDexCacheError if the optimized dex file + * is stale but exists on a read-only partition. + */ + native public static boolean isDexOptNeeded(String fileName) + throws FileNotFoundException, IOException; +} diff --git a/libart/src/main/java/dalvik/system/DexPathList.java b/libart/src/main/java/dalvik/system/DexPathList.java new file mode 100644 index 0000000..5518c83 --- /dev/null +++ b/libart/src/main/java/dalvik/system/DexPathList.java @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2011 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 dalvik.system; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.regex.Pattern; +import java.util.zip.ZipFile; +import libcore.io.ErrnoException; +import libcore.io.IoUtils; +import libcore.io.Libcore; +import libcore.io.StructStat; +import static libcore.io.OsConstants.*; + +/** + * A pair of lists of entries, associated with a {@code ClassLoader}. + * One of the lists is a dex/resource path — typically referred + * to as a "class path" — list, and the other names directories + * containing native code libraries. Class path entries may be any of: + * a {@code .jar} or {@code .zip} file containing an optional + * top-level {@code classes.dex} file as well as arbitrary resources, + * or a plain {@code .dex} file (with no possibility of associated + * resources). + * + *

This class also contains methods to use these lists to look up + * classes and resources.

+ */ +/*package*/ final class DexPathList { + private static final String DEX_SUFFIX = ".dex"; + private static final String JAR_SUFFIX = ".jar"; + private static final String ZIP_SUFFIX = ".zip"; + private static final String APK_SUFFIX = ".apk"; + + /** class definition context */ + private final ClassLoader definingContext; + + /** + * List of dex/resource (class path) elements. + * Should be called pathElements, but the Facebook app uses reflection + * to modify 'dexElements' (http://b/7726934). + */ + private final Element[] dexElements; + + /** List of native library directories. */ + private final File[] nativeLibraryDirectories; + + /** + * Constructs an instance. + * + * @param definingContext the context in which any as-yet unresolved + * classes should be defined + * @param dexPath list of dex/resource path elements, separated by + * {@code File.pathSeparator} + * @param libraryPath list of native library directory path elements, + * separated by {@code File.pathSeparator} + * @param optimizedDirectory directory where optimized {@code .dex} files + * should be found and written to, or {@code null} to use the default + * system directory for same + */ + public DexPathList(ClassLoader definingContext, String dexPath, + String libraryPath, File optimizedDirectory) { + if (definingContext == null) { + throw new NullPointerException("definingContext == null"); + } + + if (dexPath == null) { + throw new NullPointerException("dexPath == null"); + } + + if (optimizedDirectory != null) { + if (!optimizedDirectory.exists()) { + throw new IllegalArgumentException( + "optimizedDirectory doesn't exist: " + + optimizedDirectory); + } + + if (!(optimizedDirectory.canRead() + && optimizedDirectory.canWrite())) { + throw new IllegalArgumentException( + "optimizedDirectory not readable/writable: " + + optimizedDirectory); + } + } + + this.definingContext = definingContext; + this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory); + this.nativeLibraryDirectories = splitLibraryPath(libraryPath); + } + + @Override public String toString() { + return "DexPathList[" + Arrays.toString(dexElements) + + ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectories) + "]"; + } + + /** + * For BaseDexClassLoader.getLdLibraryPath. + */ + public File[] getNativeLibraryDirectories() { + return nativeLibraryDirectories; + } + + /** + * Splits the given dex path string into elements using the path + * separator, pruning out any elements that do not refer to existing + * and readable files. (That is, directories are not included in the + * result.) + */ + private static ArrayList splitDexPath(String path) { + return splitPaths(path, null, false); + } + + /** + * Splits the given library directory path string into elements + * using the path separator ({@code File.pathSeparator}, which + * defaults to {@code ":"} on Android, appending on the elements + * from the system library path, and pruning out any elements that + * do not refer to existing and readable directories. + */ + private static File[] splitLibraryPath(String path) { + /* + * Native libraries may exist in both the system and + * application library paths, and we use this search order: + * + * 1. this class loader's library path for application + * libraries + * 2. the VM's library path from the system + * property for system libraries + * + * This order was reversed prior to Gingerbread; see http://b/2933456. + */ + ArrayList result = splitPaths(path, System.getProperty("java.library.path"), true); + return result.toArray(new File[result.size()]); + } + + /** + * Splits the given path strings into file elements using the path + * separator, combining the results and filtering out elements + * that don't exist, aren't readable, or aren't either a regular + * file or a directory (as specified). Either string may be empty + * or {@code null}, in which case it is ignored. If both strings + * are empty or {@code null}, or all elements get pruned out, then + * this returns a zero-element list. + */ + private static ArrayList splitPaths(String path1, String path2, + boolean wantDirectories) { + ArrayList result = new ArrayList(); + + splitAndAdd(path1, wantDirectories, result); + splitAndAdd(path2, wantDirectories, result); + return result; + } + + /** + * Helper for {@link #splitPaths}, which does the actual splitting + * and filtering and adding to a result. + */ + private static void splitAndAdd(String searchPath, boolean directoriesOnly, + ArrayList resultList) { + if (searchPath == null) { + return; + } + for (String path : searchPath.split(":")) { + try { + StructStat sb = Libcore.os.stat(path); + if (!directoriesOnly || S_ISDIR(sb.st_mode)) { + resultList.add(new File(path)); + } + } catch (ErrnoException ignored) { + } + } + } + + /** + * Makes an array of dex/resource path elements, one per element of + * the given array. + */ + private static Element[] makeDexElements(ArrayList files, + File optimizedDirectory) { + ArrayList elements = new ArrayList(); + + /* + * Open all files and load the (direct or contained) dex files + * up front. + */ + for (File file : files) { + File zip = null; + DexFile dex = null; + String name = file.getName(); + + if (name.endsWith(DEX_SUFFIX)) { + // Raw dex file (not inside a zip/jar). + try { + dex = loadDexFile(file, optimizedDirectory); + } catch (IOException ex) { + System.logE("Unable to load dex file: " + file, ex); + } + } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX) + || name.endsWith(ZIP_SUFFIX)) { + zip = file; + + try { + dex = loadDexFile(file, optimizedDirectory); + } catch (IOException ignored) { + /* + * IOException might get thrown "legitimately" by + * the DexFile constructor if the zip file turns + * out to be resource-only (that is, no + * classes.dex file in it). Safe to just ignore + * the exception here, and let dex == null. + */ + } + } else if (file.isDirectory()) { + // We support directories for looking up resources. + // This is only useful for running libcore tests. + elements.add(new Element(file, true, null, null)); + } else { + System.logW("Unknown file type for: " + file); + } + + if ((zip != null) || (dex != null)) { + elements.add(new Element(file, false, zip, dex)); + } + } + + return elements.toArray(new Element[elements.size()]); + } + + /** + * Constructs a {@code DexFile} instance, as appropriate depending + * on whether {@code optimizedDirectory} is {@code null}. + */ + private static DexFile loadDexFile(File file, File optimizedDirectory) + throws IOException { + if (optimizedDirectory == null) { + return new DexFile(file); + } else { + String optimizedPath = optimizedPathFor(file, optimizedDirectory); + return DexFile.loadDex(file.getPath(), optimizedPath, 0); + } + } + + /** + * Converts a dex/jar file path and an output directory to an + * output file path for an associated oat file. + */ + private static String optimizedPathFor(File path, + File optimizedDirectory) { + String fileName = path.getName() + ".oat"; + File result = new File(optimizedDirectory, fileName); + return result.getPath(); + } + + /** + * Finds the named class in one of the dex files pointed at by + * this instance. This will find the one in the earliest listed + * path element. If the class is found but has not yet been + * defined, then this method will define it in the defining + * context that this instance was constructed with. + * + * @param name of class to find + * @param suppressed exceptions encountered whilst finding the class + * @return the named class or {@code null} if the class is not + * found in any of the dex files + */ + public Class findClass(String name, List suppressed) { + for (Element element : dexElements) { + DexFile dex = element.dexFile; + + if (dex != null) { + Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); + if (clazz != null) { + return clazz; + } + } + } + + return null; + } + + /** + * Finds the named resource in one of the zip/jar files pointed at + * by this instance. This will find the one in the earliest listed + * path element. + * + * @return a URL to the named resource or {@code null} if the + * resource is not found in any of the zip/jar files + */ + public URL findResource(String name) { + for (Element element : dexElements) { + URL url = element.findResource(name); + if (url != null) { + return url; + } + } + + return null; + } + + /** + * Finds all the resources with the given name, returning an + * enumeration of them. If there are no resources with the given + * name, then this method returns an empty enumeration. + */ + public Enumeration findResources(String name) { + ArrayList result = new ArrayList(); + + for (Element element : dexElements) { + URL url = element.findResource(name); + if (url != null) { + result.add(url); + } + } + + return Collections.enumeration(result); + } + + /** + * Finds the named native code library on any of the library + * directories pointed at by this instance. This will find the + * one in the earliest listed directory, ignoring any that are not + * readable regular files. + * + * @return the complete path to the library or {@code null} if no + * library was found + */ + public String findLibrary(String libraryName) { + String fileName = System.mapLibraryName(libraryName); + for (File directory : nativeLibraryDirectories) { + String path = new File(directory, fileName).getPath(); + if (IoUtils.canOpenReadOnly(path)) { + return path; + } + } + return null; + } + + /** + * Element of the dex/resource file path + */ + /*package*/ static class Element { + private final File file; + private final boolean isDirectory; + private final File zip; + private final DexFile dexFile; + + private ZipFile zipFile; + private boolean initialized; + + public Element(File file, boolean isDirectory, File zip, DexFile dexFile) { + this.file = file; + this.isDirectory = isDirectory; + this.zip = zip; + this.dexFile = dexFile; + } + + @Override public String toString() { + if (isDirectory) { + return "directory \"" + file + "\""; + } else if (zip != null) { + return "zip file \"" + zip + "\""; + } else { + return "dex file \"" + dexFile + "\""; + } + } + + public synchronized void maybeInit() { + if (initialized) { + return; + } + + initialized = true; + + if (isDirectory || zip == null) { + return; + } + + try { + zipFile = new ZipFile(zip); + } catch (IOException ioe) { + /* + * Note: ZipException (a subclass of IOException) + * might get thrown by the ZipFile constructor + * (e.g. if the file isn't actually a zip/jar + * file). + */ + System.logE("Unable to open zip file: " + file, ioe); + zipFile = null; + } + } + + public URL findResource(String name) { + maybeInit(); + + // We support directories so we can run tests and/or legacy code + // that uses Class.getResource. + if (isDirectory) { + File resourceFile = new File(file, name); + if (resourceFile.exists()) { + try { + return resourceFile.toURI().toURL(); + } catch (MalformedURLException ex) { + throw new RuntimeException(ex); + } + } + } + + if (zipFile == null || zipFile.getEntry(name) == null) { + /* + * Either this element has no zip/jar file (first + * clause), or the zip/jar file doesn't have an entry + * for the given name (second clause). + */ + return null; + } + + try { + /* + * File.toURL() is compliant with RFC 1738 in + * always creating absolute path names. If we + * construct the URL by concatenating strings, we + * might end up with illegal URLs for relative + * names. + */ + return new URL("jar:" + file.toURL() + "!/" + name); + } catch (MalformedURLException ex) { + throw new RuntimeException(ex); + } + } + } +} diff --git a/libart/src/main/java/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java new file mode 100644 index 0000000..9e3f74c --- /dev/null +++ b/libart/src/main/java/java/lang/Class.java @@ -0,0 +1,1630 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * Copyright (C) 2006-2007 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 java.lang; + +import com.android.dex.Dex; +import dalvik.system.VMStack; +import java.io.InputStream; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.reflect.AbstractMethod; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.net.URL; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import libcore.reflect.AnnotationAccess; +import libcore.reflect.GenericSignatureParser; +import libcore.reflect.InternalNames; +import libcore.reflect.Types; +import libcore.util.CollectionUtils; +import libcore.util.EmptyArray; + +/** + * The in-memory representation of a Java class. This representation serves as + * the starting point for querying class-related information, a process usually + * called "reflection". There are basically three types of {@code Class} + * instances: those representing real classes and interfaces, those representing + * primitive types, and those representing array classes. + * + *

Class instances representing object types (classes or interfaces)

+ *

+ * These represent an ordinary class or interface as found in the class + * hierarchy. The name associated with these {@code Class} instances is simply + * the fully qualified class name of the class or interface that it represents. + * In addition to this human-readable name, each class is also associated by a + * so-called descriptor, which is the letter "L", followed by the + * class name and a semicolon (";"). The descriptor is what the runtime system + * uses internally for identifying the class (for example in a DEX file). + *

+ *

Classes representing primitive types

+ *

+ * These represent the standard Java primitive types and hence share their + * names (for example "int" for the {@code int} primitive type). Although it is + * not possible to create new instances based on these {@code Class} instances, + * they are still useful for providing reflection information, and as the + * component type of array classes. There is one {@code Class} instance for each + * primitive type, and their descriptors are: + *

+ *
    + *
  • {@code B} representing the {@code byte} primitive type
  • + *
  • {@code S} representing the {@code short} primitive type
  • + *
  • {@code I} representing the {@code int} primitive type
  • + *
  • {@code J} representing the {@code long} primitive type
  • + *
  • {@code F} representing the {@code float} primitive type
  • + *
  • {@code D} representing the {@code double} primitive type
  • + *
  • {@code C} representing the {@code char} primitive type
  • + *
  • {@code Z} representing the {@code boolean} primitive type
  • + *
  • {@code V} representing void function return values
  • + *
+ *

+ *

Classes representing array classes

+ *

+ * These represent the classes of Java arrays. There is one such {@code Class} + * instance per combination of array leaf component type and arity (number of + * dimensions). In this case, the name associated with the {@code Class} + * consists of one or more left square brackets (one per dimension in the array) + * followed by the descriptor of the class representing the leaf component type, + * which can be either an object type or a primitive type. The descriptor of a + * {@code Class} representing an array type is the same as its name. Examples + * of array class descriptors are: + *

+ *
    + *
  • {@code [I} representing the {@code int[]} type
  • + *
  • {@code [Ljava/lang/String;} representing the {@code String[]} type
  • + *
  • {@code [[[C} representing the {@code char[][][]} type (three dimensions!)
  • + *
+ */ +public final class Class implements Serializable, AnnotatedElement, GenericDeclaration, Type { + + private static final long serialVersionUID = 3206093459760846163L; + + /** defining class loader, or NULL for the "bootstrap" system loader. */ + private transient ClassLoader classLoader; + + /** + * For array classes, the component class object for instanceof/checkcast (for String[][][], + * this will be String[][]). NULL for non-array classes. + */ + private transient Class componentType; + /** + * DexCache of resolved constant pool entries. Will be null for certain VM-generated classes + * e.g. arrays and primitive classes. + */ + private transient DexCache dexCache; + + /** static, private, and <init> methods. */ + private transient AbstractMethod[] directMethods; + + /** + * Instance fields. These describe the layout of the contents of an Object. Note that only the + * fields directly declared by this class are listed in iFields; fields declared by a + * superclass are listed in the superclass's Class.iFields. + * + * All instance fields that refer to objects are guaranteed to be at the beginning of the field + * list. {@link Class#numReferenceInstanceFields} specifies the number of reference fields. + */ + private transient Field[] iFields; + + /** + * The interface table (iftable_) contains pairs of a interface class and an array of the + * interface methods. There is one pair per interface supported by this class. That + * means one pair for each interface we support directly, indirectly via superclass, or + * indirectly via a superinterface. This will be null if neither we nor our superclass + * implement any interfaces. + * + * Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()". + * Invoke faceObj.blah(), where "blah" is part of the Face interface. We can't easily use a + * single vtable. + * + * For every interface a concrete class implements, we create an array of the concrete vtable_ + * methods for the methods in the interface. + */ + private transient Object[] ifTable; + + /** Lazily computed name of this class; always prefer calling getName(). */ + private transient String name; + + /** Static fields */ + private transient Field[] sFields; + + /** The superclass, or NULL if this is java.lang.Object, an interface or primitive type. */ + private transient Class superClass; + + /** If class verify fails, we must return same error on subsequent tries. */ + private transient Class verifyErrorClass; + + /** Virtual methods defined in this class; invoked through vtable. */ + private transient Method[] virtualMethods; + + /** + * Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass + * is copied in, and virtual methods from our class either replace those from the super or are + * appended. For abstract classes, methods may be created in the vtable that aren't in + * virtual_ methods_ for miranda methods. + */ + private transient Method[] vtable; + + /** access flags; low 16 bits are defined by VM spec */ + private transient int accessFlags; + + /** + * Total size of the Class instance; used when allocating storage on GC heap. + * See also {@link Class#objectSize}. + */ + private transient int classSize; + + /** + * tid used to check for recursive static initializer invocation. + */ + private transient int clinitThreadId; + + /** + * Type index from dex file. + * TODO: really 16bits + */ + private transient int dexTypeIndex; + + /** Number of instance fields that are object references. */ + private transient int numReferenceInstanceFields; + + /** Number of static fields that are object references. */ + private transient int numReferenceStaticFields; + + /** + * Total object size; used when allocating storage on GC heap. For interfaces and abstract + * classes this will be zero. See also {@link Class#classSize}. + */ + private transient int objectSize; + + /** Primitive type value, or 0 if not a primitive type; set for generated primitive classes. */ + private transient int primitiveType; + + /** Bitmap of offsets of iFields. */ + private transient int referenceInstanceOffsets; + + /** Bitmap of offsets of sFields. */ + private transient int referenceStaticOffsets; + + /** State of class initialization */ + private transient int status; + + private Class() { + // Prevent this class to be instantiated, instance should be created by JVM only + } + + /** + * Returns a {@code Class} object which represents the class with the + * given name. The name should be the name of a non-primitive class, as described in + * the {@link Class class definition}. + * Primitive types can not be found using this method; use {@code int.class} or {@code Integer.TYPE} instead. + * + *

If the class has not yet been loaded, it is loaded and initialized + * first. This is done through either the class loader of the calling class + * or one of its parent class loaders. It is possible that a static initializer is run as + * a result of this call. + * + * @throws ClassNotFoundException + * if the requested class cannot be found. + * @throws LinkageError + * if an error occurs during linkage + * @throws ExceptionInInitializerError + * if an exception occurs during static initialization of a + * class. + */ + public static Class forName(String className) throws ClassNotFoundException { + return forName(className, true, VMStack.getCallingClassLoader()); + } + + /** + * Returns a {@code Class} object which represents the class with the + * given name. The name should be the name of a non-primitive class, as described in + * the {@link Class class definition}. + * Primitive types can not be found using this method; use {@code int.class} or {@code Integer.TYPE} instead. + * + *

If the class has not yet been loaded, it is loaded first, using the given class loader. + * If the class has not yet been initialized and {@code shouldInitialize} is true, + * the class will be initialized. + * + * @throws ClassNotFoundException + * if the requested class cannot be found. + * @throws LinkageError + * if an error occurs during linkage + * @throws ExceptionInInitializerError + * if an exception occurs during static initialization of a + * class. + */ + public static Class forName(String className, boolean shouldInitialize, + ClassLoader classLoader) throws ClassNotFoundException { + + if (classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + // Catch an Exception thrown by the underlying native code. It wraps + // up everything inside a ClassNotFoundException, even if e.g. an + // Error occurred during initialization. This as a workaround for + // an ExceptionInInitializerError that's also wrapped. It is actually + // expected to be thrown. Maybe the same goes for other errors. + // Not wrapping up all the errors will break android though. + Class result; + try { + result = classForName(className, shouldInitialize, classLoader); + } catch (ClassNotFoundException e) { + Throwable cause = e.getCause(); + if (cause instanceof LinkageError) { + throw (LinkageError) cause; + } + throw e; + } + return result; + } + + static native Class classForName(String className, boolean shouldInitialize, + ClassLoader classLoader) throws ClassNotFoundException; + + /** + * Returns an array containing {@code Class} objects for all public classes, + * interfaces, enums and annotations that are members of this class and its + * superclasses. This does not include classes of implemented interfaces. + * If there are no such class members or if this object represents a primitive type then an array + * of length 0 is returned. + */ + public Class[] getClasses() { + List> result = new ArrayList>(); + for (Class c = this; c != null; c = c.superClass) { + for (Class member : c.getDeclaredClasses()) { + if (Modifier.isPublic(member.getModifiers())) { + result.add(member); + } + } + } + return result.toArray(new Class[result.size()]); + } + + @Override public A getAnnotation(Class annotationType) { + return AnnotationAccess.getAnnotation(this, annotationType); + } + + /** + * Returns an array containing all the annotations of this class. If there are no annotations + * then an empty array is returned. + * + * @see #getDeclaredAnnotations() + */ + @Override public Annotation[] getAnnotations() { + return AnnotationAccess.getAnnotations(this); + } + + /** + * Returns the canonical name of this class. If this class does not have a + * canonical name as defined in the Java Language Specification, then the + * method returns {@code null}. + */ + public String getCanonicalName() { + if (isLocalClass() || isAnonymousClass()) + return null; + + if (isArray()) { + /* + * The canonical name of an array type depends on the (existence of) + * the component type's canonical name. + */ + String name = getComponentType().getCanonicalName(); + if (name != null) { + return name + "[]"; + } + } else if (isMemberClass()) { + /* + * The canonical name of an inner class depends on the (existence + * of) the declaring class' canonical name. + */ + String name = getDeclaringClass().getCanonicalName(); + if (name != null) { + return name + "." + getSimpleName(); + } + } else { + /* + * The canonical name of a top-level class or primitive type is + * equal to the fully qualified name. + */ + return getName(); + } + + /* + * Other classes don't have a canonical name. + */ + return null; + } + + /** + * Returns the class loader which was used to load the class represented by + * this {@code Class}. Implementations are free to return {@code null} for + * classes that were loaded by the bootstrap class loader. The Android + * reference implementation, though, always returns a reference to an actual + * class loader. + */ + public ClassLoader getClassLoader() { + if (this.isPrimitive()) { + return null; + } + + ClassLoader loader = getClassLoaderImpl(); + if (loader == null) { + loader = BootClassLoader.getInstance(); + } + return loader; + } + + /** + * This must be provided by the VM vendor, as it is used by other provided + * class implementations in this package. Outside of this class, it is used + * by SecurityManager.classLoaderDepth(), + * currentClassLoader() and currentLoadedClass(). Return the ClassLoader for + * this Class without doing any security checks. The bootstrap ClassLoader + * is returned, unlike getClassLoader() which returns null in place of the + * bootstrap ClassLoader. + */ + ClassLoader getClassLoaderImpl() { + ClassLoader loader = classLoader; + return loader == null ? BootClassLoader.getInstance() : loader; + } + + /** + * Returns a {@code Class} object which represents the component type if + * this class represents an array type. Returns {@code null} if this class + * does not represent an array type. The component type of an array type is + * the type of the elements of the array. + */ + public Class getComponentType() { + return componentType; + } + + /** + * Returns the dex file from which this class was loaded. + * + * @hide + */ + public native Dex getDex(); + + /** + * Returns a string from the dex cache, computing the string from the dex file if necessary + * + * @hide + */ + public String getDexCacheString(Dex dex, int dexStringIndex) { + String[] dexCacheStrings = dexCache.strings; + String s = dexCacheStrings[dexStringIndex]; + if (s == null) { + s = dex.strings().get(dexStringIndex); + dexCacheStrings[dexStringIndex] = s; + } + return s; + } + + /** + * Returns a resolved type from the dex cache, computing the string from the dex file if + * necessary + * + * @hide + */ + public Class getDexCacheType(Dex dex, int dexTypeIndex) { + Class[] dexCacheResolvedTypes = dexCache.resolvedTypes; + Class resolvedType = dexCacheResolvedTypes[dexTypeIndex]; + if (resolvedType == null) { + int descriptorIndex = dex.typeIds().get(dexTypeIndex); + String descriptor = getDexCacheString(dex, descriptorIndex); + resolvedType = InternalNames.getClass(getClassLoader(), descriptor); + dexCacheResolvedTypes[dexTypeIndex] = resolvedType; + } + return resolvedType; + } + + /** + * Returns a {@code Constructor} object which represents the public + * constructor matching the given parameter types. + * {@code (Class[]) null} is equivalent to the empty array. + * + * @throws NoSuchMethodException + * if the constructor cannot be found. + * @see #getDeclaredConstructor(Class[]) + */ + public Constructor getConstructor(Class... parameterTypes) throws NoSuchMethodException { + return getConstructor(parameterTypes, true); + } + + /** + * Returns a {@code Constructor} object which represents the constructor + * matching the specified parameter types that is declared by the class + * represented by this {@code Class}. + * {@code (Class[]) null} is equivalent to the empty array. + * + * @throws NoSuchMethodException + * if the requested constructor cannot be found. + * @see #getConstructor(Class[]) + */ + public Constructor getDeclaredConstructor(Class... parameterTypes) + throws NoSuchMethodException { + return getConstructor(parameterTypes, false); + } + + /** + * Returns a constructor with the given parameters. + * + * @param publicOnly true to only return public constructores. + * @param parameterTypes argument types to match the constructor's. + */ + private Constructor getConstructor(Class[] parameterTypes, boolean publicOnly) + throws NoSuchMethodException { + if (parameterTypes == null) { + parameterTypes = EmptyArray.CLASS; + } + for (Class c : parameterTypes) { + if (c == null) { + throw new NoSuchMethodException("parameter type is null"); + } + } + Constructor result = getDeclaredConstructorInternal(parameterTypes); + if (result == null || publicOnly && !Modifier.isPublic(result.getAccessFlags())) { + throw new NoSuchMethodException(" " + Arrays.toString(parameterTypes)); + } + return result; + } + + /** + * Returns the constructor with the given parameters if it is defined by this class; null + * otherwise. This may return a non-public member. + * + * @param args the types of the parameters to the constructor. + */ + private Constructor getDeclaredConstructorInternal(Class[] args) { + if (directMethods != null) { + for (AbstractMethod m : directMethods) { + if (m instanceof Constructor && Arrays.equals(args, m.getParameterTypes())) { + return (Constructor) m; + } + } + } + return null; + } + + /** + * Returns an array containing {@code Constructor} objects for all public + * constructors for this {@code Class}. If there + * are no public constructors or if this {@code Class} represents an array + * class, a primitive type or void then an empty array is returned. + * + * @see #getDeclaredConstructors() + */ + public Constructor[] getConstructors() { + ArrayList> constructors = new ArrayList(); + getDeclaredConstructors(true, constructors); + return constructors.toArray(new Constructor[constructors.size()]); + } + + /** + * Returns an array containing {@code Constructor} objects for all + * constructors declared in the class represented by this {@code Class}. If + * there are no constructors or if this {@code Class} represents an array + * class, a primitive type or void then an empty array is returned. + * + * @see #getConstructors() + */ + public Constructor[] getDeclaredConstructors() { + ArrayList> constructors = new ArrayList(); + getDeclaredConstructors(false, constructors); + return constructors.toArray(new Constructor[constructors.size()]); + } + + private void getDeclaredConstructors(boolean publicOnly, List> constructors) { + if (directMethods != null) { + for (AbstractMethod m : directMethods) { + int modifiers = m.getAccessFlags(); + if (!publicOnly || Modifier.isPublic(modifiers)) { + if (m instanceof Constructor) { + constructors.add((Constructor) m); + } + } + } + } + } + + /** + * Returns a {@code Method} object which represents the method matching the + * specified name and parameter types that is declared by the class + * represented by this {@code Class}. + * + * @param name + * the requested method's name. + * @param parameterTypes + * the parameter types of the requested method. + * {@code (Class[]) null} is equivalent to the empty array. + * @return the method described by {@code name} and {@code parameterTypes}. + * @throws NoSuchMethodException + * if the requested constructor cannot be found. + * @throws NullPointerException + * if {@code name} is {@code null}. + * @see #getMethod(String, Class[]) + */ + public Method getDeclaredMethod(String name, Class... parameterTypes) + throws NoSuchMethodException { + return getMethod(name, parameterTypes, false); + } + + /** + * Returns a {@code Method} object which represents the public method with + * the specified name and parameter types. + * {@code (Class[]) null} is equivalent to the empty array. + * This method first searches the + * class C represented by this {@code Class}, then the superclasses of C and + * finally the interfaces implemented by C and finally the superclasses of C + * for a method with matching name. + * + * @throws NoSuchMethodException + * if the method cannot be found. + * @see #getDeclaredMethod(String, Class[]) + */ + public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException { + return getMethod(name, parameterTypes, true); + } + + private Method getMethod(String name, Class[] parameterTypes, boolean recursivePublicMethods) + throws NoSuchMethodException { + if (name == null) { + throw new NullPointerException("name == null"); + } + if (parameterTypes == null) { + parameterTypes = EmptyArray.CLASS; + } + for (Class c : parameterTypes) { + if (c == null) { + throw new NoSuchMethodException("parameter type is null"); + } + } + Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes) + : getDeclaredMethodInternal(name, parameterTypes); + // Fail if we didn't find the method or it was expected to be public. + if (result == null || + (recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) { + throw new NoSuchMethodException(name + " " + Arrays.toString(parameterTypes)); + } + return result; + } + + private Method getPublicMethodRecursive(String name, Class[] parameterTypes) { + // search superclasses + for (Class c = this; c != null; c = c.getSuperclass()) { + Method result = c.getDeclaredMethodInternal(name, parameterTypes); + if (result != null && Modifier.isPublic(result.getAccessFlags())) { + return result; + } + } + // search iftable which has a flattened and uniqued list of interfaces + Object[] iftable = ifTable; + if (iftable != null) { + for (int i = 0; i < iftable.length; i += 2) { + Class ifc = (Class) iftable[i]; + Method result = ifc.getPublicMethodRecursive(name, parameterTypes); + if (result != null && Modifier.isPublic(result.getAccessFlags())) { + return result; + } + } + } + return null; + } + + /** + * Returns the method if it is defined by this class; null otherwise. This may return a + * non-public member. + * + * @param name the method name + * @param args the method's parameter types + */ + private Method getDeclaredMethodInternal(String name, Class[] args) { + // Covariant return types permit the class to define multiple + // methods with the same name and parameter types. Prefer to + // return a non-synthetic method in such situations. We may + // still return a synthetic method to handle situations like + // escalated visibility. We never return miranda methods that + // were synthesized by the VM. + int skipModifiers = Modifier.MIRANDA | Modifier.SYNTHETIC; + AbstractMethod result = null; + if (directMethods != null) { + for (AbstractMethod m : directMethods) { + if (name.equals(m.getName()) && Arrays.equals(args, m.getParameterTypes()) && + m instanceof Method) { + int modifiers = m.getAccessFlags(); + if ((modifiers & skipModifiers) == 0) { + return (Method) m; + } else { + // Direct methods cannot be miranda methods, + // so this potential result must be synthetic. + result = m; + } + } + } + } + if (result == null) { + if (virtualMethods != null) { + for (Method m : virtualMethods) { + if (name.equals(m.getName()) && Arrays.equals(args, m.getParameterTypes())) { + int modifiers = m.getAccessFlags(); + if ((modifiers & skipModifiers) == 0) { + return (Method) m; + } else if ((modifiers & Modifier.MIRANDA) == 0) { + // Remember as potential result if it's not a miranda method. + result = m; + } + } + } + } + } + return (Method) result; + } + + /** + * Returns an array containing {@code Method} objects for all methods + * declared in the class represented by this {@code Class}. If there are no + * methods or if this {@code Class} represents an array class, a primitive + * type or void then an empty array is returned. + * + * @see #getMethods() + */ + public Method[] getDeclaredMethods() { + int initial_size = virtualMethods == null ? 0 : virtualMethods.length; + initial_size += directMethods == null ? 0 : directMethods.length; + ArrayList methods = new ArrayList(initial_size); + getDeclaredMethods(false, methods); + Method[] result = methods.toArray(new Method[methods.size()]); + for (Method m : result) { + // Throw NoClassDefFoundError if types cannot be resolved. + m.getReturnType(); + m.getParameterTypes(); + } + return result; + + } + + /** + * Returns the list of methods without performing any security checks + * first. If no methods exist, an empty array is returned. + */ + private void getDeclaredMethods(boolean publicOnly, List methods) { + if (virtualMethods != null) { + for (Method m : virtualMethods) { + int modifiers = m.getAccessFlags(); + if (!publicOnly || Modifier.isPublic(modifiers)) { + // Add non-miranda virtual methods. + if ((modifiers & Modifier.MIRANDA) == 0) { + methods.add((Method) m); + } + } + } + } + if (directMethods != null) { + for (AbstractMethod m : directMethods) { + int modifiers = m.getAccessFlags(); + if (!publicOnly || Modifier.isPublic(modifiers)) { + // Add non-constructor direct/static methods. + if (m instanceof Method) { + methods.add((Method) m); + } + } + } + } + } + + /** + * Returns an array containing {@code Method} objects for all public methods + * for the class C represented by this {@code Class}. Methods may be + * declared in C, the interfaces it implements or in the superclasses of C. + * The elements in the returned array are in no particular order. + * + *

If there are no public methods or if this {@code Class} represents a + * primitive type or {@code void} then an empty array is returned. + * + * @see #getDeclaredMethods() + */ + public Method[] getMethods() { + List methods = new ArrayList(); + getPublicMethodsInternal(methods); + /* + * Remove duplicate methods defined by superclasses and + * interfaces, preferring to keep methods declared by derived + * types. + */ + CollectionUtils.removeDuplicates(methods, Method.ORDER_BY_SIGNATURE); + return methods.toArray(new Method[methods.size()]); + } + + /** + * Populates {@code result} with public methods defined by this class, its + * superclasses, and all implemented interfaces, including overridden methods. + */ + private void getPublicMethodsInternal(List result) { + getDeclaredMethods(true, result); + if (!isInterface()) { + // Search superclasses, for interfaces don't search java.lang.Object. + for (Class c = superClass; c != null; c = c.superClass) { + c.getDeclaredMethods(true, result); + } + } + // Search iftable which has a flattened and uniqued list of interfaces. + Object[] iftable = ifTable; + if (iftable != null) { + for (int i = 0; i < iftable.length; i += 2) { + Class ifc = (Class) iftable[i]; + ifc.getDeclaredMethods(true, result); + } + } + } + + /** + * Returns the annotations that are directly defined on the class + * represented by this {@code Class}. Annotations that are inherited are not + * included in the result. If there are no annotations at all, an empty + * array is returned. + * + * @see #getAnnotations() + */ + @Override public Annotation[] getDeclaredAnnotations() { + List result = AnnotationAccess.getDeclaredAnnotations(this); + return result.toArray(new Annotation[result.size()]); + } + + /** + * Returns an array containing {@code Class} objects for all classes, + * interfaces, enums and annotations that are members of this class. + */ + public Class[] getDeclaredClasses() { + return AnnotationAccess.getMemberClasses(this); + } + + /** + * Returns a {@code Field} object for the field with the given name + * which is declared in the class represented by this {@code Class}. + * + * @throws NoSuchFieldException if the requested field can not be found. + * @see #getField(String) + */ + public Field getDeclaredField(String name) throws NoSuchFieldException { + if (name == null) { + throw new NullPointerException("name == null"); + } + Field result = getDeclaredFieldInternal(name); + if (result == null) { + throw new NoSuchFieldException(name); + } else { + result.getType(); // Throw NoClassDefFoundError if type cannot be resolved. + } + return result; + } + + /** + * Returns an array containing {@code Field} objects for all fields declared + * in the class represented by this {@code Class}. If there are no fields or + * if this {@code Class} represents an array class, a primitive type or void + * then an empty array is returned. + * + * @see #getFields() + */ + public Field[] getDeclaredFields() { + int initial_size = sFields == null ? 0 : sFields.length; + initial_size += iFields == null ? 0 : iFields.length; + ArrayList fields = new ArrayList(initial_size); + getDeclaredFields(false, fields); + Field[] result = fields.toArray(new Field[fields.size()]); + for (Field f : result) { + f.getType(); // Throw NoClassDefFoundError if type cannot be resolved. + } + return result; + } + + private void getDeclaredFields(boolean publicOnly, List fields) { + if (iFields != null) { + for (Field f : iFields) { + if (!publicOnly || Modifier.isPublic(f.getModifiers())) { + fields.add(f); + } + } + } + if (sFields != null) { + for (Field f : sFields) { + if (!publicOnly || Modifier.isPublic(f.getModifiers())) { + fields.add(f); + } + } + } + } + + /** + * Returns the field if it is defined by this class; null otherwise. This + * may return a non-public member. + */ + private Field getDeclaredFieldInternal(String name) { + if (iFields != null) { + for (Field f : iFields) { + if (f.getName().equals(name)) { + return f; + } + } + } + if (sFields != null) { + for (Field f : sFields) { + if (f.getName().equals(name)) { + return f; + } + } + } + return null; + } + + /** + * Returns the class that this class is a member of, or {@code null} if this + * class is a top-level class, a primitive, an array, or defined within a + * method or constructor. + */ + public Class getDeclaringClass() { + return AnnotationAccess.getDeclaringClass(this); + } + + /** + * Returns the class enclosing this class. For most classes this is the same + * as the {@link #getDeclaringClass() declaring class}. For classes defined + * within a method or constructor (typically anonymous inner classes), this + * is the declaring class of that member. + */ + public Class getEnclosingClass() { + Class declaringClass = getDeclaringClass(); + if (declaringClass != null) { + return declaringClass; + } + AccessibleObject member = AnnotationAccess.getEnclosingMethodOrConstructor(this); + return member != null + ? ((Member) member).getDeclaringClass() + : null; + } + + /** + * Returns the enclosing {@code Constructor} of this {@code Class}, if it is an + * anonymous or local/automatic class; otherwise {@code null}. + */ + public Constructor getEnclosingConstructor() { + if (classNameImpliesTopLevel()) { + return null; + } + AccessibleObject result = AnnotationAccess.getEnclosingMethodOrConstructor(this); + return result instanceof Constructor ? (Constructor) result : null; + } + + /** + * Returns the enclosing {@code Method} of this {@code Class}, if it is an + * anonymous or local/automatic class; otherwise {@code null}. + */ + public Method getEnclosingMethod() { + if (classNameImpliesTopLevel()) { + return null; + } + AccessibleObject result = AnnotationAccess.getEnclosingMethodOrConstructor(this); + return result instanceof Method ? (Method) result : null; + } + + /** + * Returns true if this class is definitely a top level class, or false if + * a more expensive check like {@link #getEnclosingClass()} is necessary. + * + *

This is a hack that exploits an implementation detail of all Java + * language compilers: generated names always contain "$". As it is possible + * for a top level class to be named with a "$", a false result does + * not indicate that this isn't a top-level class. + */ + private boolean classNameImpliesTopLevel() { + return !getName().contains("$"); + } + + /** + * Returns the {@code enum} constants associated with this {@code Class}. + * Returns {@code null} if this {@code Class} does not represent an {@code + * enum} type. + */ + @SuppressWarnings("unchecked") // we only cast after confirming that this class is an enum + public T[] getEnumConstants() { + if (!isEnum()) { + return null; + } + return (T[]) Enum.getSharedConstants((Class) this).clone(); + } + + /** + * Returns a {@code Field} object which represents the public field with the + * given name. This method first searches the class C represented by + * this {@code Class}, then the interfaces implemented by C and finally the + * superclasses of C. + * + * @throws NoSuchFieldException + * if the field cannot be found. + * @see #getDeclaredField(String) + */ + public Field getField(String name) throws NoSuchFieldException { + if (name == null) { + throw new NullPointerException("name == null"); + } + Field result = getPublicFieldRecursive(name); + if (result == null) { + throw new NoSuchFieldException(name); + } else { + result.getType(); // Throw NoClassDefFoundError if type cannot be resolved. + } + return result; + } + + private Field getPublicFieldRecursive(String name) { + // search superclasses + for (Class c = this; c != null; c = c.superClass) { + Field result = c.getDeclaredFieldInternal(name); + if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) { + return result; + } + } + + // search iftable which has a flattened and uniqued list of interfaces + if (ifTable != null) { + for (int i = 0; i < ifTable.length; i += 2) { + Class ifc = (Class) ifTable[i]; + Field result = ifc.getPublicFieldRecursive(name); + if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) { + return result; + } + } + } + + return null; + } + + /** + * Returns an array containing {@code Field} objects for all public fields + * for the class C represented by this {@code Class}. Fields may be declared + * in C, the interfaces it implements or in the superclasses of C. The + * elements in the returned array are in no particular order. + * + *

If there are no public fields or if this class represents an array class, + * a primitive type or {@code void} then an empty array is returned. + * + * @see #getDeclaredFields() + */ + public Field[] getFields() { + List fields = new ArrayList(); + getPublicFieldsRecursive(fields); + Field[] result = fields.toArray(new Field[fields.size()]); + for (Field f : result) { + f.getType(); // Throw NoClassDefFoundError if type cannot be resolved. + } + return result; + } + + /** + * Populates {@code result} with public fields defined by this class, its + * superclasses, and all implemented interfaces. + */ + private void getPublicFieldsRecursive(List result) { + // search superclasses + for (Class c = this; c != null; c = c.superClass) { + c.getDeclaredFields(true, result); + } + + // search iftable which has a flattened and uniqued list of interfaces + Object[] iftable = ifTable; + if (iftable != null) { + for (int i = 0; i < iftable.length; i += 2) { + Class ifc = (Class) iftable[i]; + ifc.getDeclaredFields(true, result); + } + } + } + + /** + * Returns the {@link Type}s of the interfaces that this {@code Class} directly + * implements. If the {@code Class} represents a primitive type or {@code + * void} then an empty array is returned. + */ + public Type[] getGenericInterfaces() { + GenericSignatureParser parser = new GenericSignatureParser(getClassLoader()); + parser.parseForClass(this, AnnotationAccess.getSignature(this)); + return Types.getClonedTypeArray(parser.interfaceTypes); + } + + /** + * Returns the {@code Type} that represents the superclass of this {@code + * class}. + */ + public Type getGenericSuperclass() { + GenericSignatureParser parser = new GenericSignatureParser(getClassLoader()); + parser.parseForClass(this, AnnotationAccess.getSignature(this)); + return Types.getType(parser.superclassType); + } + + /** + * Returns an array of {@code Class} objects that match the interfaces + * in the {@code implements} declaration of the class represented + * by this {@code Class}. The order of the elements in the array is + * identical to the order in the original class declaration. If the class + * does not implement any interfaces, an empty array is returned. + * + *

This method only returns directly-implemented interfaces, and does not + * include interfaces implemented by superclasses or superinterfaces of any + * implemented interfaces. + */ + public Class[] getInterfaces() { + if (isArray()) { + return new Class[] { Cloneable.class, Serializable.class }; + } else if (isProxy()) { + return getProxyInterfaces(); + } else { + return AnnotationAccess.typeIndexToInterfaces(this, getDex(), getTypeIndex()); + } + } + + // Returns the interfaces that this proxy class directly implements. + private native Class[] getProxyInterfaces(); + + /** + * Returns an integer that represents the modifiers of the class represented + * by this {@code Class}. The returned value is a combination of bits + * defined by constants in the {@link Modifier} class. + */ + public int getModifiers() { + int JAVA_FLAGS_MASK = 0xffff; + int modifiers = AnnotationAccess.getInnerClassFlags(this, accessFlags & JAVA_FLAGS_MASK); + return modifiers & JAVA_FLAGS_MASK; + } + + /** + * Returns the name of the class represented by this {@code Class}. For a + * description of the format which is used, see the class definition of + * {@link Class}. + */ + public String getName() { + String result = name; + return (result == null) ? (name = getNameNative()) : result; + } + + private native String getNameNative(); + + /** + * Returns the simple name of the class represented by this {@code Class} as + * defined in the source code. If there is no name (that is, the class is + * anonymous) then an empty string is returned. If the receiver is an array + * then the name of the underlying type with square braces appended (for + * example {@code "Integer[]"}) is returned. + * + * @return the simple name of the class represented by this {@code Class}. + */ + public String getSimpleName() { + if (isArray()) { + return getComponentType().getSimpleName() + "[]"; + } + + if (isAnonymousClass()) { + return ""; + } + + if (isMemberClass() || isLocalClass()) { + return getInnerClassName(); + } + + String name = getName(); + int dot = name.lastIndexOf('.'); + if (dot != -1) { + return name.substring(dot + 1); + } + + return name; + } + + /** + * Returns the simple name of a member or local class, or null otherwise. + */ + private String getInnerClassName() { + return AnnotationAccess.getInnerClassName(this); + } + + /** + * Returns null. + */ + public ProtectionDomain getProtectionDomain() { + return null; + } + + /** + * Returns the URL of the given resource, or null if the resource is not found. + * The mapping between the resource name and the URL is managed by the class' class loader. + * + * @see ClassLoader + */ + public URL getResource(String resourceName) { + // Get absolute resource name, but without the leading slash + if (resourceName.startsWith("/")) { + resourceName = resourceName.substring(1); + } else { + String pkg = getName(); + int dot = pkg.lastIndexOf('.'); + if (dot != -1) { + pkg = pkg.substring(0, dot).replace('.', '/'); + } else { + pkg = ""; + } + + resourceName = pkg + "/" + resourceName; + } + + // Delegate to proper class loader + ClassLoader loader = getClassLoader(); + if (loader != null) { + return loader.getResource(resourceName); + } else { + return ClassLoader.getSystemResource(resourceName); + } + } + + /** + * Returns a read-only stream for the contents of the given resource, or null if the resource + * is not found. + * The mapping between the resource name and the stream is managed by the class' class loader. + * + * @see ClassLoader + */ + public InputStream getResourceAsStream(String resourceName) { + // Get absolute resource name, but without the leading slash + if (resourceName.startsWith("/")) { + resourceName = resourceName.substring(1); + } else { + String pkg = getName(); + int dot = pkg.lastIndexOf('.'); + if (dot != -1) { + pkg = pkg.substring(0, dot).replace('.', '/'); + } else { + pkg = ""; + } + + resourceName = pkg + "/" + resourceName; + } + + // Delegate to proper class loader + ClassLoader loader = getClassLoader(); + if (loader != null) { + return loader.getResourceAsStream(resourceName); + } else { + return ClassLoader.getSystemResourceAsStream(resourceName); + } + } + + /** + * Returns null. (On Android, a {@code ClassLoader} can load classes from multiple dex files. + * All classes from any given dex file will have the same signers, but different dex + * files may have different signers. This does not fit well with the original + * {@code ClassLoader}-based model of {@code getSigners}.) + */ + public Object[] getSigners() { + // See http://code.google.com/p/android/issues/detail?id=1766. + return null; + } + + /** + * Returns the {@code Class} object which represents the superclass of the + * class represented by this {@code Class}. If this {@code Class} represents + * the {@code Object} class, a primitive type, an interface or void then the + * method returns {@code null}. If this {@code Class} represents an array + * class then the {@code Object} class is returned. + */ + public Class getSuperclass() { + // For interfaces superClass is Object (which agrees with the JNI spec) + // but not with the expected behavior here. + if (isInterface()) { + return null; + } else { + return superClass; + } + } + + /** + * Returns an array containing {@code TypeVariable} objects for type + * variables declared by the generic class represented by this {@code + * Class}. Returns an empty array if the class is not generic. + */ + @SuppressWarnings("unchecked") + @Override public synchronized TypeVariable>[] getTypeParameters() { + GenericSignatureParser parser = new GenericSignatureParser(getClassLoader()); + parser.parseForClass(this, AnnotationAccess.getSignature(this)); + return parser.formalTypeParameters.clone(); + } + + /** + * Tests whether this {@code Class} represents an annotation class. + */ + public boolean isAnnotation() { + final int ACC_ANNOTATION = 0x2000; // not public in reflect.Modifier + return (accessFlags & ACC_ANNOTATION) != 0; + } + + @Override public boolean isAnnotationPresent(Class annotationType) { + return AnnotationAccess.isAnnotationPresent(this, annotationType); + } + + /** + * Tests whether the class represented by this {@code Class} is + * anonymous. + */ + public boolean isAnonymousClass() { + return AnnotationAccess.isAnonymousClass(this); + } + + /** + * Tests whether the class represented by this {@code Class} is an array class. + */ + public boolean isArray() { + return getComponentType() != null; + } + + /** + * Is this a runtime created proxy class? + * + * @hide + */ + public boolean isProxy() { + return (accessFlags & 0x00040000) != 0; + } + + /** + * Can {@code c} be assigned to this class? For example, String can be assigned to Object + * (by an upcast), however, an Object cannot be assigned to a String as a potentially exception + * throwing downcast would be necessary. Similarly for interfaces, a class that implements (or + * an interface that extends) another can be assigned to its parent, but not vice-versa. All + * Classes may assign to themselves. Classes for primitive types may not assign to each other. + * + * @param c the class to check. + * @return {@code true} if {@code c} can be assigned to the class + * represented by this {@code Class}; {@code false} otherwise. + * @throws NullPointerException if {@code c} is {@code null}. + */ + public boolean isAssignableFrom(Class c) { + if (this == c) { + return true; // Can always assign to things of the same type. + } else if (this == Object.class) { + return !c.isPrimitive(); // Can assign any reference to java.lang.Object. + } else if (isArray()) { + return c.isArray() && componentType.isAssignableFrom(c.componentType); + } else if (isInterface()) { + // Search iftable which has a flattened and uniqued list of interfaces. + Object[] iftable = c.ifTable; + if (iftable != null) { + for (int i = 0; i < iftable.length; i += 2) { + Class ifc = (Class) iftable[i]; + if (ifc == this) { + return true; + } + } + } + return false; + } else { + if (!c.isInterface()) { + for (c = c.superClass; c != null; c = c.superClass) { + if (c == this) { + return true; + } + } + } + return false; + } + } + + /** + * Tests whether the class represented by this {@code Class} is an + * {@code enum}. + */ + public boolean isEnum() { + return ((getModifiers() & 0x4000) != 0) && (getSuperclass() == Enum.class); + } + + /** + * Tests whether the given object can be cast to the class + * represented by this {@code Class}. This is the runtime version of the + * {@code instanceof} operator. + * + * @return {@code true} if {@code object} can be cast to the type + * represented by this {@code Class}; {@code false} if {@code + * object} is {@code null} or cannot be cast. + */ + public boolean isInstance(Object object) { + if (object == null) { + return false; + } + return isAssignableFrom(object.getClass()); + } + + /** + * Tests whether this {@code Class} represents an interface. + */ + public boolean isInterface() { + return (accessFlags & Modifier.INTERFACE) != 0; + } + + /** + * Tests whether the class represented by this {@code Class} is defined + * locally. + */ + public boolean isLocalClass() { + return !classNameImpliesTopLevel() + && AnnotationAccess.getEnclosingMethodOrConstructor(this) != null + && !isAnonymousClass(); + } + + /** + * Tests whether the class represented by this {@code Class} is a member + * class. + */ + public boolean isMemberClass() { + return getDeclaringClass() != null; + } + + /** + * Tests whether this {@code Class} represents a primitive type. + */ + public boolean isPrimitive() { + return primitiveType != 0; + } + + /** + * Tests whether this {@code Class} represents a synthetic type. + */ + public boolean isSynthetic() { + final int ACC_SYNTHETIC = 0x1000; // not public in reflect.Modifier + return (accessFlags & ACC_SYNTHETIC) != 0; + } + + /** + * Indicates whether this {@code Class} or its parents override finalize. + * + * @hide + */ + public boolean isFinalizable() { + final int ACC_CLASS_IS_FINALIZABLE = 0x80000000; // not public in reflect.Modifier + return (accessFlags & ACC_CLASS_IS_FINALIZABLE) != 0; + } + + /** + * Returns a new instance of the class represented by this {@code Class}, + * created by invoking the default (that is, zero-argument) constructor. If + * there is no such constructor, or if the creation fails (either because of + * a lack of available memory or because an exception is thrown by the + * constructor), an {@code InstantiationException} is thrown. If the default + * constructor exists but is not accessible from the context where this + * method is invoked, an {@code IllegalAccessException} is thrown. + * + * @throws IllegalAccessException + * if the default constructor is not visible. + * @throws InstantiationException + * if the instance cannot be created. + */ + public T newInstance() throws InstantiationException, IllegalAccessException { + if (isPrimitive() || isInterface() || isArray() || Modifier.isAbstract(accessFlags)) { + throw new InstantiationException(this + " cannot be instantiated"); + } + Class caller = VMStack.getStackClass1(); + if (!caller.canAccess(this)) { + throw new IllegalAccessException(this + " is not accessible from " + caller); + } + Constructor init; + try { + init = getDeclaredConstructor(); + } catch (NoSuchMethodException e) { + InstantiationException t = + new InstantiationException(this + " has no zero argument constructor"); + t.initCause(e); + throw t; + } + if (!caller.canAccessMember(this, init.getAccessFlags())) { + throw new IllegalAccessException(init + " is not accessible from " + caller); + } + try { + return init.newInstance(); + } catch (InvocationTargetException e) { + InstantiationException t = new InstantiationException(this); + t.initCause(e); + throw t; + } + } + + private boolean canAccess(Class c) { + if(Modifier.isPublic(c.accessFlags)) { + return true; + } + return inSamePackage(c); + } + + private boolean canAccessMember(Class memberClass, int memberModifiers) { + if (memberClass == this || Modifier.isPublic(memberModifiers)) { + return true; + } + if (Modifier.isPrivate(memberModifiers)) { + return false; + } + if (Modifier.isProtected(memberModifiers)) { + for (Class parent = this.superClass; parent != null; parent = parent.superClass) { + if (parent == memberClass) { + return true; + } + } + } + return inSamePackage(memberClass); + } + + private boolean inSamePackage(Class c) { + if (classLoader != c.classLoader) { + return false; + } + String packageName1 = getPackageName$(); + String packageName2 = c.getPackageName$(); + if (packageName1 == null) { + return packageName2 == null; + } else if (packageName2 == null) { + return false; + } else { + return packageName1.equals(packageName2); + } + } + + @Override + public String toString() { + if (isPrimitive()) { + return getSimpleName(); + } else { + return (isInterface() ? "interface " : "class ") + getName(); + } + } + + /** + * Returns the {@code Package} of which the class represented by this + * {@code Class} is a member. Returns {@code null} if no {@code Package} + * object was created by the class loader of the class. + */ + public Package getPackage() { + // TODO This might be a hack, but the VM doesn't have the necessary info. + ClassLoader loader = getClassLoader(); + if (loader != null) { + String packageName = getPackageName$(); + return packageName != null ? loader.getPackage(packageName) : null; + } + return null; + } + + /** + * Returns the package name of this class. This returns null for classes in + * the default package. + * + * @hide + */ + public String getPackageName$() { + String name = getName(); + int last = name.lastIndexOf('.'); + return last == -1 ? null : name.substring(0, last); + } + + /** + * Returns the assertion status for the class represented by this {@code + * Class}. Assertion is enabled / disabled based on the class loader, + * package or class default at runtime. + */ + public boolean desiredAssertionStatus() { + return false; + } + + /** + * Casts this {@code Class} to represent a subclass of the given class. + * If successful, this {@code Class} is returned; otherwise a {@code + * ClassCastException} is thrown. + * + * @throws ClassCastException + * if this {@code Class} cannot be cast to the given type. + */ + @SuppressWarnings("unchecked") + public Class asSubclass(Class c) { + if (c.isAssignableFrom(this)) { + return (Class)this; + } + String actualClassName = this.getName(); + String desiredClassName = c.getName(); + throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName); + } + + /** + * Casts the given object to the type represented by this {@code Class}. + * If the object is {@code null} then the result is also {@code null}. + * + * @throws ClassCastException + * if the object cannot be cast to the given type. + */ + @SuppressWarnings("unchecked") + public T cast(Object obj) { + if (obj == null) { + return null; + } else if (this.isInstance(obj)) { + return (T)obj; + } + String actualClassName = obj.getClass().getName(); + String desiredClassName = this.getName(); + throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName); + } + + /** + * The type index of this class in its own Dex, or 0 if it is unknown. If a + * class is referenced by multiple Dex files, it will have a different type + * index in each. Dex files support 65534 type indices, with 65535 + * representing no index. + * + * TODO: 0 is a valid index; this should be -1 if it is unknown + * + * @hide + */ + public int getTypeIndex() { + int result = dexTypeIndex; + if (result == 0) { // uncomputed => Dalvik + result = AnnotationAccess.computeTypeIndex(getDex(), this); + dexTypeIndex = result; + } + return result; + } + + /** + * The annotation directory offset of this class in its own Dex, or 0 if it + * is unknown. + * + * TODO: 0 is a sentinel that means 'no annotations directory'; this should be -1 if unknown + * + * @hide + */ + public native int getAnnotationDirectoryOffset(); + // TODO: Use native code rather than: + // return AnnotationAccess.typeIndexToAnnotationDirectoryOffset(getDex(), getTypeIndex()); + // as native code has access to fast maps between type indices and class + // defs. Move fast maps to managed code. +} diff --git a/libart/src/main/java/java/lang/ClassLoader.java b/libart/src/main/java/java/lang/ClassLoader.java new file mode 100644 index 0000000..fb2eb8f --- /dev/null +++ b/libart/src/main/java/java/lang/ClassLoader.java @@ -0,0 +1,856 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * Copyright (C) 2008 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 java.lang; + +import dalvik.system.PathClassLoader; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.ByteBuffer; +import java.security.ProtectionDomain; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Loads classes and resources from a repository. One or more class loaders are + * installed at runtime. These are consulted whenever the runtime system needs a + * specific class that is not yet available in-memory. Typically, class loaders + * are grouped into a tree where child class loaders delegate all requests to + * parent class loaders. Only if the parent class loader cannot satisfy the + * request, the child class loader itself tries to handle it. + *

+ * {@code ClassLoader} is an abstract class that implements the common + * infrastructure required by all class loaders. Android provides several + * concrete implementations of the class, with + * {@link dalvik.system.PathClassLoader} being the one typically used. Other + * applications may implement subclasses of {@code ClassLoader} to provide + * special ways for loading classes. + *

+ * @see Class + */ +public abstract class ClassLoader { + + /** + * The 'System' ClassLoader - the one that is responsible for loading + * classes from the classpath. It is not equal to the bootstrap class loader - + * that one handles the built-in classes. + * + * Because of a potential class initialization race between ClassLoader and + * java.lang.System, reproducible when using JDWP with "suspend=y", we defer + * creation of the system class loader until first use. We use a static + * inner class to get synchronization at init time without having to sync on + * every access. + * + * @see #getSystemClassLoader() + */ + static private class SystemClassLoader { + public static ClassLoader loader = ClassLoader.createSystemClassLoader(); + } + + /** + * The parent ClassLoader. + */ + private ClassLoader parent; + + /** + * The packages known to the class loader. + */ + private Map packages = new HashMap(); + + /** + * To avoid unloading individual classes, {@link java.lang.reflect.Proxy} + * only generates one class for each set of interfaces. This maps sets of + * interfaces to the proxy class that implements all of them. It is declared + * here so that these generated classes can be unloaded with their class + * loader. + * + * @hide + */ + public final Map>, Class> proxyCache + = Collections.synchronizedMap(new HashMap>, Class>()); + + /** + * Create the system class loader. Note this is NOT the bootstrap class + * loader (which is managed by the VM). We use a null value for the parent + * to indicate that the bootstrap loader is our parent. + */ + private static ClassLoader createSystemClassLoader() { + String classPath = System.getProperty("java.class.path", "."); + + // String[] paths = classPath.split(":"); + // URL[] urls = new URL[paths.length]; + // for (int i = 0; i < paths.length; i++) { + // try { + // urls[i] = new URL("file://" + paths[i]); + // } + // catch (Exception ex) { + // ex.printStackTrace(); + // } + // } + // + // return new java.net.URLClassLoader(urls, null); + + // TODO Make this a java.net.URLClassLoader once we have those? + return new PathClassLoader(classPath, BootClassLoader.getInstance()); + } + + /** + * Returns the system class loader. This is the parent for new + * {@code ClassLoader} instances and is typically the class loader used to + * start the application. + */ + public static ClassLoader getSystemClassLoader() { + return SystemClassLoader.loader; + } + + /** + * Finds the URL of the resource with the specified name. The system class + * loader's resource lookup algorithm is used to find the resource. + * + * @return the {@code URL} object for the requested resource or {@code null} + * if the resource can not be found. + * @param resName + * the name of the resource to find. + * @see Class#getResource + */ + public static URL getSystemResource(String resName) { + return SystemClassLoader.loader.getResource(resName); + } + + /** + * Returns an enumeration of URLs for the resource with the specified name. + * The system class loader's resource lookup algorithm is used to find the + * resource. + * + * @return an enumeration of {@code URL} objects containing the requested + * resources. + * @param resName + * the name of the resource to find. + * @throws IOException + * if an I/O error occurs. + */ + public static Enumeration getSystemResources(String resName) throws IOException { + return SystemClassLoader.loader.getResources(resName); + } + + /** + * Returns a stream for the resource with the specified name. The system + * class loader's resource lookup algorithm is used to find the resource. + * Basically, the contents of the java.class.path are searched in order, + * looking for a path which matches the specified resource. + * + * @return a stream for the resource or {@code null}. + * @param resName + * the name of the resource to find. + * @see Class#getResourceAsStream + */ + public static InputStream getSystemResourceAsStream(String resName) { + return SystemClassLoader.loader.getResourceAsStream(resName); + } + + /** + * Constructs a new instance of this class with the system class loader as + * its parent. + */ + protected ClassLoader() { + this(getSystemClassLoader(), false); + } + + /** + * Constructs a new instance of this class with the specified class loader + * as its parent. + * + * @param parentLoader + * The {@code ClassLoader} to use as the new class loader's + * parent. + */ + protected ClassLoader(ClassLoader parentLoader) { + this(parentLoader, false); + } + + /* + * constructor for the BootClassLoader which needs parent to be null. + */ + ClassLoader(ClassLoader parentLoader, boolean nullAllowed) { + if (parentLoader == null && !nullAllowed) { + throw new NullPointerException("parentLoader == null && !nullAllowed"); + } + parent = parentLoader; + } + + /** + * Constructs a new class from an array of bytes containing a class + * definition in class file format. + * + * @param classRep + * the memory image of a class file. + * @param offset + * the offset into {@code classRep}. + * @param length + * the length of the class file. + * @return the {@code Class} object created from the specified subset of + * data in {@code classRep}. + * @throws ClassFormatError + * if {@code classRep} does not contain a valid class. + * @throws IndexOutOfBoundsException + * if {@code offset < 0}, {@code length < 0} or if + * {@code offset + length} is greater than the length of + * {@code classRep}. + * @deprecated Use {@link #defineClass(String, byte[], int, int)} + */ + @Deprecated + protected final Class defineClass(byte[] classRep, int offset, int length) + throws ClassFormatError { + throw new UnsupportedOperationException("can't load this type of class file"); + } + + /** + * Constructs a new class from an array of bytes containing a class + * definition in class file format. + * + * @param className + * the expected name of the new class, may be {@code null} if not + * known. + * @param classRep + * the memory image of a class file. + * @param offset + * the offset into {@code classRep}. + * @param length + * the length of the class file. + * @return the {@code Class} object created from the specified subset of + * data in {@code classRep}. + * @throws ClassFormatError + * if {@code classRep} does not contain a valid class. + * @throws IndexOutOfBoundsException + * if {@code offset < 0}, {@code length < 0} or if + * {@code offset + length} is greater than the length of + * {@code classRep}. + */ + protected final Class defineClass(String className, byte[] classRep, int offset, int length) + throws ClassFormatError { + throw new UnsupportedOperationException("can't load this type of class file"); + } + + /** + * Constructs a new class from an array of bytes containing a class + * definition in class file format and assigns the specified protection + * domain to the new class. If the provided protection domain is + * {@code null} then a default protection domain is assigned to the class. + * + * @param className + * the expected name of the new class, may be {@code null} if not + * known. + * @param classRep + * the memory image of a class file. + * @param offset + * the offset into {@code classRep}. + * @param length + * the length of the class file. + * @param protectionDomain + * the protection domain to assign to the loaded class, may be + * {@code null}. + * @return the {@code Class} object created from the specified subset of + * data in {@code classRep}. + * @throws ClassFormatError + * if {@code classRep} does not contain a valid class. + * @throws IndexOutOfBoundsException + * if {@code offset < 0}, {@code length < 0} or if + * {@code offset + length} is greater than the length of + * {@code classRep}. + * @throws NoClassDefFoundError + * if {@code className} is not equal to the name of the class + * contained in {@code classRep}. + */ + protected final Class defineClass(String className, byte[] classRep, int offset, int length, + ProtectionDomain protectionDomain) throws java.lang.ClassFormatError { + throw new UnsupportedOperationException("can't load this type of class file"); + } + + /** + * Defines a new class with the specified name, byte code from the byte + * buffer and the optional protection domain. If the provided protection + * domain is {@code null} then a default protection domain is assigned to + * the class. + * + * @param name + * the expected name of the new class, may be {@code null} if not + * known. + * @param b + * the byte buffer containing the byte code of the new class. + * @param protectionDomain + * the protection domain to assign to the loaded class, may be + * {@code null}. + * @return the {@code Class} object created from the data in {@code b}. + * @throws ClassFormatError + * if {@code b} does not contain a valid class. + * @throws NoClassDefFoundError + * if {@code className} is not equal to the name of the class + * contained in {@code b}. + */ + protected final Class defineClass(String name, ByteBuffer b, + ProtectionDomain protectionDomain) throws ClassFormatError { + + byte[] temp = new byte[b.remaining()]; + b.get(temp); + return defineClass(name, temp, 0, temp.length, protectionDomain); + } + + /** + * Overridden by subclasses, throws a {@code ClassNotFoundException} by + * default. This method is called by {@code loadClass} after the parent + * {@code ClassLoader} has failed to find a loaded class of the same name. + * + * @param className + * the name of the class to look for. + * @return the {@code Class} object that is found. + * @throws ClassNotFoundException + * if the class cannot be found. + */ + protected Class findClass(String className) throws ClassNotFoundException { + throw new ClassNotFoundException(className); + } + + /** + * Returns the class with the specified name if it has already been loaded + * by the VM or {@code null} if it has not yet been loaded. + * + * @param className + * the name of the class to look for. + * @return the {@code Class} object or {@code null} if the requested class + * has not been loaded. + */ + protected final Class findLoadedClass(String className) { + ClassLoader loader; + if (this == BootClassLoader.getInstance()) + loader = null; + else + loader = this; + return VMClassLoader.findLoadedClass(loader, className); + } + + /** + * Finds the class with the specified name, loading it using the system + * class loader if necessary. + * + * @param className + * the name of the class to look for. + * @return the {@code Class} object with the requested {@code className}. + * @throws ClassNotFoundException + * if the class can not be found. + */ + protected final Class findSystemClass(String className) throws ClassNotFoundException { + return Class.forName(className, false, getSystemClassLoader()); + } + + /** + * Returns this class loader's parent. + * + * @return this class loader's parent or {@code null}. + */ + public final ClassLoader getParent() { + return parent; + } + + /** + * Returns the URL of the resource with the specified name. This + * implementation first tries to use the parent class loader to find the + * resource; if this fails then {@link #findResource(String)} is called to + * find the requested resource. + * + * @param resName + * the name of the resource to find. + * @return the {@code URL} object for the requested resource or {@code null} + * if the resource can not be found + * @see Class#getResource + */ + public URL getResource(String resName) { + URL resource = parent.getResource(resName); + if (resource == null) { + resource = findResource(resName); + } + return resource; + } + + /** + * Returns an enumeration of URLs for the resource with the specified name. + * This implementation first uses this class loader's parent to find the + * resource, then it calls {@link #findResources(String)} to get additional + * URLs. The returned enumeration contains the {@code URL} objects of both + * find operations. + * + * @return an enumeration of {@code URL} objects for the requested resource. + * @param resName + * the name of the resource to find. + * @throws IOException + * if an I/O error occurs. + */ + @SuppressWarnings("unchecked") + public Enumeration getResources(String resName) throws IOException { + + Enumeration first = parent.getResources(resName); + Enumeration second = findResources(resName); + + return new TwoEnumerationsInOne(first, second); + } + + /** + * Returns a stream for the resource with the specified name. See + * {@link #getResource(String)} for a description of the lookup algorithm + * used to find the resource. + * + * @return a stream for the resource or {@code null} if the resource can not be found + * @param resName + * the name of the resource to find. + * @see Class#getResourceAsStream + */ + public InputStream getResourceAsStream(String resName) { + try { + URL url = getResource(resName); + if (url != null) { + return url.openStream(); + } + } catch (IOException ex) { + // Don't want to see the exception. + } + + return null; + } + + /** + * Loads the class with the specified name. Invoking this method is + * equivalent to calling {@code loadClass(className, false)}. + *

+ * Note: In the Android reference implementation, the + * second parameter of {@link #loadClass(String, boolean)} is ignored + * anyway. + *

+ * + * @return the {@code Class} object. + * @param className + * the name of the class to look for. + * @throws ClassNotFoundException + * if the class can not be found. + */ + public Class loadClass(String className) throws ClassNotFoundException { + return loadClass(className, false); + } + + /** + * Loads the class with the specified name, optionally linking it after + * loading. The following steps are performed: + *
    + *
  1. Call {@link #findLoadedClass(String)} to determine if the requested + * class has already been loaded.
  2. + *
  3. If the class has not yet been loaded: Invoke this method on the + * parent class loader.
  4. + *
  5. If the class has still not been loaded: Call + * {@link #findClass(String)} to find the class.
  6. + *
+ *

+ * Note: In the Android reference implementation, the + * {@code resolve} parameter is ignored; classes are never linked. + *

+ * + * @return the {@code Class} object. + * @param className + * the name of the class to look for. + * @param resolve + * Indicates if the class should be resolved after loading. This + * parameter is ignored on the Android reference implementation; + * classes are not resolved. + * @throws ClassNotFoundException + * if the class can not be found. + */ + protected Class loadClass(String className, boolean resolve) throws ClassNotFoundException { + Class clazz = findLoadedClass(className); + + if (clazz == null) { + ClassNotFoundException suppressed = null; + try { + clazz = parent.loadClass(className, false); + } catch (ClassNotFoundException e) { + suppressed = e; + } + + if (clazz == null) { + try { + clazz = findClass(className); + } catch (ClassNotFoundException e) { + e.addSuppressed(suppressed); + throw e; + } + } + } + + return clazz; + } + + /** + * Forces a class to be linked (initialized). If the class has already been + * linked this operation has no effect. + *

+ * Note: In the Android reference implementation, this + * method has no effect. + *

+ * + * @param clazz + * the class to link. + */ + protected final void resolveClass(Class clazz) { + // no-op, doesn't make sense on android. + } + + /** + * Finds the URL of the resource with the specified name. This + * implementation just returns {@code null}; it should be overridden in + * subclasses. + * + * @param resName + * the name of the resource to find. + * @return the {@code URL} object for the requested resource. + */ + protected URL findResource(String resName) { + return null; + } + + /** + * Finds an enumeration of URLs for the resource with the specified name. + * This implementation just returns an empty {@code Enumeration}; it should + * be overridden in subclasses. + * + * @param resName + * the name of the resource to find. + * @return an enumeration of {@code URL} objects for the requested resource. + * @throws IOException + * if an I/O error occurs. + */ + @SuppressWarnings( { + "unchecked", "unused" + }) + protected Enumeration findResources(String resName) throws IOException { + return Collections.emptyEnumeration(); + } + + /** + * Returns the absolute path of the native library with the specified name, + * or {@code null}. If this method returns {@code null} then the virtual + * machine searches the directories specified by the system property + * "java.library.path". + *

+ * This implementation always returns {@code null}. + *

+ * + * @param libName + * the name of the library to find. + * @return the absolute path of the library. + */ + protected String findLibrary(String libName) { + return null; + } + + /** + * Returns the package with the specified name. Package information is + * searched in this class loader. + * + * @param name + * the name of the package to find. + * @return the package with the requested name; {@code null} if the package + * can not be found. + */ + protected Package getPackage(String name) { + synchronized (packages) { + return packages.get(name); + } + } + + /** + * Returns all the packages known to this class loader. + * + * @return an array with all packages known to this class loader. + */ + protected Package[] getPackages() { + synchronized (packages) { + Collection col = packages.values(); + Package[] result = new Package[col.size()]; + col.toArray(result); + return result; + } + } + + /** + * Defines and returns a new {@code Package} using the specified + * information. If {@code sealBase} is {@code null}, the package is left + * unsealed. Otherwise, the package is sealed using this URL. + * + * @param name + * the name of the package. + * @param specTitle + * the title of the specification. + * @param specVersion + * the version of the specification. + * @param specVendor + * the vendor of the specification. + * @param implTitle + * the implementation title. + * @param implVersion + * the implementation version. + * @param implVendor + * the specification vendor. + * @param sealBase + * the URL used to seal this package or {@code null} to leave the + * package unsealed. + * @return the {@code Package} object that has been created. + * @throws IllegalArgumentException + * if a package with the specified name already exists. + */ + protected Package definePackage(String name, String specTitle, String specVersion, + String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) + throws IllegalArgumentException { + + synchronized (packages) { + if (packages.containsKey(name)) { + throw new IllegalArgumentException("Package " + name + " already defined"); + } + + Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle, + implVersion, implVendor, sealBase); + + packages.put(name, newPackage); + + return newPackage; + } + } + + /** + * Sets the signers of the specified class. This implementation does + * nothing. + * + * @param c + * the {@code Class} object for which to set the signers. + * @param signers + * the signers for {@code c}. + */ + protected final void setSigners(Class c, Object[] signers) { + } + + /** + * Sets the assertion status of the class with the specified name. + *

+ * Note: This method does nothing in the Android reference + * implementation. + *

+ * + * @param cname + * the name of the class for which to set the assertion status. + * @param enable + * the new assertion status. + */ + public void setClassAssertionStatus(String cname, boolean enable) { + } + + /** + * Sets the assertion status of the package with the specified name. + *

+ * Note: This method does nothing in the Android reference + * implementation. + *

+ * + * @param pname + * the name of the package for which to set the assertion status. + * @param enable + * the new assertion status. + */ + public void setPackageAssertionStatus(String pname, boolean enable) { + } + + /** + * Sets the default assertion status for this class loader. + *

+ * Note: This method does nothing in the Android reference + * implementation. + *

+ * + * @param enable + * the new assertion status. + */ + public void setDefaultAssertionStatus(boolean enable) { + } + + /** + * Sets the default assertion status for this class loader to {@code false} + * and removes any package default and class assertion status settings. + *

+ * Note: This method does nothing in the Android reference + * implementation. + *

+ */ + public void clearAssertionStatus() { + } +} + +/* + * Provides a helper class that combines two existing URL enumerations into one. + * It is required for the getResources() methods. Items are fetched from the + * first enumeration until it's empty, then from the second one. + */ +class TwoEnumerationsInOne implements Enumeration { + + private final Enumeration first; + + private final Enumeration second; + + public TwoEnumerationsInOne(Enumeration first, Enumeration second) { + this.first = first; + this.second = second; + } + + @Override + public boolean hasMoreElements() { + return first.hasMoreElements() || second.hasMoreElements(); + } + + @Override + public URL nextElement() { + if (first.hasMoreElements()) { + return first.nextElement(); + } else { + return second.nextElement(); + } + } + +} + +/** + * Provides an explicit representation of the boot class loader. It sits at the + * head of the class loader chain and delegates requests to the VM's internal + * class loading mechanism. + */ +class BootClassLoader extends ClassLoader { + + private static BootClassLoader instance; + + @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED") + public static synchronized BootClassLoader getInstance() { + if (instance == null) { + instance = new BootClassLoader(); + } + + return instance; + } + + public BootClassLoader() { + super(null, true); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + return Class.classForName(name, false, null); + } + + @Override + protected URL findResource(String name) { + return VMClassLoader.getResource(name); + } + + @SuppressWarnings("unused") + @Override + protected Enumeration findResources(String resName) throws IOException { + return Collections.enumeration(VMClassLoader.getResources(resName)); + } + + /** + * Returns package information for the given package. Unfortunately, the + * Android BootClassLoader doesn't really have this information, and as a + * non-secure ClassLoader, it isn't even required to, according to the spec. + * Yet, we want to provide it, in order to make all those hopeful callers of + * {@code myClass.getPackage().getName()} happy. Thus we construct a Package + * object the first time it is being requested and fill most of the fields + * with dummy values. The Package object is then put into the ClassLoader's + * Package cache, so we see the same one next time. We don't create Package + * objects for null arguments or for the default package. + *

+ * There a limited chance that we end up with multiple Package objects + * representing the same package: It can happen when when a package is + * scattered across different JAR files being loaded by different + * ClassLoaders. Rather unlikely, and given that this whole thing is more or + * less a workaround, probably not worth the effort. + */ + @Override + protected Package getPackage(String name) { + if (name != null && !name.isEmpty()) { + synchronized (this) { + Package pack = super.getPackage(name); + + if (pack == null) { + pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0", + "Unknown", null); + } + + return pack; + } + } + + return null; + } + + @Override + public URL getResource(String resName) { + return findResource(resName); + } + + @Override + protected Class loadClass(String className, boolean resolve) + throws ClassNotFoundException { + Class clazz = findLoadedClass(className); + + if (clazz == null) { + clazz = findClass(className); + } + + return clazz; + } + + @Override + public Enumeration getResources(String resName) throws IOException { + return findResources(resName); + } +} + +/** + * TODO Open issues - Missing / empty methods - Signer stuff - Protection + * domains - Assertions + */ diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java new file mode 100644 index 0000000..1422c13 --- /dev/null +++ b/libart/src/main/java/java/lang/Daemons.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2011 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 java.lang; + +import dalvik.system.VMRuntime; +import java.lang.ref.FinalizerReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.TimeoutException; +import libcore.util.EmptyArray; + +/** + * Calls Object.finalize() on objects in the finalizer reference queue. The VM + * will abort if any finalize() call takes more than the maximum finalize time + * to complete. + * + * @hide + */ +public final class Daemons { + private static final int NANOS_PER_MILLI = 1000 * 1000; + private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000; + private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND; + + public static void start() { + ReferenceQueueDaemon.INSTANCE.start(); + FinalizerDaemon.INSTANCE.start(); + FinalizerWatchdogDaemon.INSTANCE.start(); + HeapTrimmerDaemon.INSTANCE.start(); + GCDaemon.INSTANCE.start(); + } + + public static void stop() { + ReferenceQueueDaemon.INSTANCE.stop(); + FinalizerDaemon.INSTANCE.stop(); + FinalizerWatchdogDaemon.INSTANCE.stop(); + HeapTrimmerDaemon.INSTANCE.stop(); + GCDaemon.INSTANCE.stop(); + } + + /** + * A background task that provides runtime support to the application. + * Daemons can be stopped and started, but only so that the zygote can be a + * single-threaded process when it forks. + */ + private static abstract class Daemon implements Runnable { + private Thread thread; + + public synchronized void start() { + if (thread != null) { + throw new IllegalStateException("already running"); + } + thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName()); + thread.setDaemon(true); + thread.start(); + } + + public abstract void run(); + + /** + * Returns true while the current thread should continue to run; false + * when it should return. + */ + protected synchronized boolean isRunning() { + return thread != null; + } + + public synchronized void interrupt() { + if (thread == null) { + throw new IllegalStateException("not running"); + } + thread.interrupt(); + } + + /** + * Waits for the runtime thread to stop. This interrupts the thread + * currently running the runnable and then waits for it to exit. + */ + public void stop() { + Thread threadToStop; + synchronized (this) { + threadToStop = thread; + thread = null; + } + if (threadToStop == null) { + throw new IllegalStateException("not running"); + } + threadToStop.interrupt(); + while (true) { + try { + threadToStop.join(); + return; + } catch (InterruptedException ignored) { + } + } + } + + /** + * Returns the current stack trace of the thread, or an empty stack trace + * if the thread is not currently running. + */ + public synchronized StackTraceElement[] getStackTrace() { + return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT; + } + } + + /** + * This heap management thread moves elements from the garbage collector's + * pending list to the managed reference queue. + */ + private static class ReferenceQueueDaemon extends Daemon { + private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon(); + + @Override public void run() { + while (isRunning()) { + Reference list; + try { + synchronized (ReferenceQueue.class) { + while (ReferenceQueue.unenqueued == null) { + ReferenceQueue.class.wait(); + } + list = ReferenceQueue.unenqueued; + ReferenceQueue.unenqueued = null; + } + } catch (InterruptedException e) { + continue; + } + enqueue(list); + } + } + + private void enqueue(Reference list) { + while (list != null) { + Reference reference; + // pendingNext is owned by the GC so no synchronization is required + if (list == list.pendingNext) { + reference = list; + reference.pendingNext = null; + list = null; + } else { + reference = list.pendingNext; + list.pendingNext = reference.pendingNext; + reference.pendingNext = null; + } + reference.enqueueInternal(); + } + } + } + + private static class FinalizerDaemon extends Daemon { + private static final FinalizerDaemon INSTANCE = new FinalizerDaemon(); + private final ReferenceQueue queue = FinalizerReference.queue; + private volatile Object finalizingObject; + private volatile long finalizingStartedNanos; + + @Override public void run() { + while (isRunning()) { + // Take a reference, blocking until one is ready or the thread should stop + try { + doFinalize((FinalizerReference) queue.remove()); + } catch (InterruptedException ignored) { + } + } + } + + @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION") + private void doFinalize(FinalizerReference reference) { + FinalizerReference.remove(reference); + Object object = reference.get(); + reference.clear(); + try { + finalizingStartedNanos = System.nanoTime(); + finalizingObject = object; + synchronized (FinalizerWatchdogDaemon.INSTANCE) { + FinalizerWatchdogDaemon.INSTANCE.notify(); + } + object.finalize(); + } catch (Throwable ex) { + // The RI silently swallows these, but Android has always logged. + System.logE("Uncaught exception thrown by finalizer", ex); + } finally { + finalizingObject = null; + } + } + } + + /** + * The watchdog exits the VM if the finalizer ever gets stuck. We consider + * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS + * on one instance. + */ + private static class FinalizerWatchdogDaemon extends Daemon { + private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon(); + + @Override public void run() { + while (isRunning()) { + Object object = waitForObject(); + if (object == null) { + // We have been interrupted, need to see if this daemon has been stopped. + continue; + } + boolean finalized = waitForFinalization(object); + if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) { + finalizerTimedOut(object); + break; + } + } + } + + private Object waitForObject() { + while (true) { + Object object = FinalizerDaemon.INSTANCE.finalizingObject; + if (object != null) { + return object; + } + synchronized (this) { + // wait until something is ready to be finalized + // http://code.google.com/p/android/issues/detail?id=22778 + try { + wait(); + } catch (InterruptedException e) { + // Daemon.stop may have interrupted us. + return null; + } + } + } + } + + private void sleepFor(long startNanos, long durationNanos) { + while (true) { + long elapsedNanos = System.nanoTime() - startNanos; + long sleepNanos = durationNanos - elapsedNanos; + long sleepMills = sleepNanos / NANOS_PER_MILLI; + if (sleepMills <= 0) { + return; + } + try { + Thread.sleep(sleepMills); + } catch (InterruptedException e) { + if (!isRunning()) { + return; + } + } + } + } + + private boolean waitForFinalization(Object object) { + sleepFor(FinalizerDaemon.INSTANCE.finalizingStartedNanos, MAX_FINALIZE_NANOS); + return object != FinalizerDaemon.INSTANCE.finalizingObject; + } + + private static void finalizerTimedOut(Object object) { + // The current object has exceeded the finalization deadline; abort! + String message = object.getClass().getName() + ".finalize() timed out after " + + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds"; + Exception syntheticException = new TimeoutException(message); + // We use the stack from where finalize() was running to show where it was stuck. + syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); + Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler(); + if (h == null) { + // If we have no handler, log and exit. + System.logE(message, syntheticException); + System.exit(2); + } + // Otherwise call the handler to do crash reporting. + // We don't just throw because we're not the thread that + // timed out; we're the thread that detected it. + h.uncaughtException(Thread.currentThread(), syntheticException); + } + } + + // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap. + public static void requestHeapTrim() { + synchronized (HeapTrimmerDaemon.INSTANCE) { + HeapTrimmerDaemon.INSTANCE.notify(); + } + } + + private static class HeapTrimmerDaemon extends Daemon { + private static final HeapTrimmerDaemon INSTANCE = new HeapTrimmerDaemon(); + + @Override public void run() { + while (isRunning()) { + try { + synchronized (this) { + wait(); + } + VMRuntime.getRuntime().trimHeap(); + } catch (InterruptedException ignored) { + } + } + } + } + + // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap. + public static void requestGC() { + GCDaemon.INSTANCE.requestGC(); + } + + private static class GCDaemon extends Daemon { + private static final GCDaemon INSTANCE = new GCDaemon(); + private int count = 0; + + public void requestGC() { + synchronized (this) { + ++count; + notify(); + } + } + + @Override public void run() { + while (isRunning()) { + try { + synchronized (this) { + // Wait until a request comes in, unless we have a pending request. + while (count == 0) { + wait(); + } + --count; + } + VMRuntime.getRuntime().concurrentGC(); + } catch (InterruptedException ignored) { + } + } + } + } +} diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java new file mode 100644 index 0000000..d0eba2f --- /dev/null +++ b/libart/src/main/java/java/lang/DexCache.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 java.lang; +import java.lang.reflect.Field; +import java.lang.reflect.AbstractMethod; + +/** + * A dex cache holds resolved copies of strings, fields, methods, and classes from the dexfile. + */ +final class DexCache { + // Only created by the VM. + private DexCache() {} + + Object[] initializedStaticStorage; + String location; + Field[] resolvedFields; + AbstractMethod[] resolvedMethods; + Class[] resolvedTypes; + String[] strings; + + // Holds pointer to dexFile. + private int dexFile; +} + diff --git a/libart/src/main/java/java/lang/Object.java b/libart/src/main/java/java/lang/Object.java new file mode 100644 index 0000000..9c59870 --- /dev/null +++ b/libart/src/main/java/java/lang/Object.java @@ -0,0 +1,452 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * Copyright (C) 2008 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 java.lang; + +/** + * The root class of the Java class hierarchy. All non-primitive types + * (including arrays) inherit either directly or indirectly from this class. + * + *

Writing a correct {@code equals} method

+ *

Follow this style to write a canonical {@code equals} method: + *

+ *   // Use @Override to avoid accidental overloading.
+ *   @Override public boolean equals(Object o) {
+ *     // Return true if the objects are identical.
+ *     // (This is just an optimization, not required for correctness.)
+ *     if (this == o) {
+ *       return true;
+ *     }
+ *
+ *     // Return false if the other object has the wrong type.
+ *     // This type may be an interface depending on the interface's specification.
+ *     if (!(o instanceof MyType)) {
+ *       return false;
+ *     }
+ *
+ *     // Cast to the appropriate type.
+ *     // This will succeed because of the instanceof, and lets us access private fields.
+ *     MyType lhs = (MyType) o;
+ *
+ *     // Check each field. Primitive fields, reference fields, and nullable reference
+ *     // fields are all treated differently.
+ *     return primitiveField == lhs.primitiveField &&
+ *             referenceField.equals(lhs.referenceField) &&
+ *             (nullableField == null ? lhs.nullableField == null
+ *                                    : nullableField.equals(lhs.nullableField));
+ *   }
+ * 
+ *

If you override {@code equals}, you should also override {@code hashCode}: equal + * instances must have equal hash codes. + * + *

See Effective Java item 8 for much more detail and clarification. + * + *

Writing a correct {@code hashCode} method

+ *

Follow this style to write a canonical {@code hashCode} method: + *

+ *   @Override public int hashCode() {
+ *     // Start with a non-zero constant.
+ *     int result = 17;
+ *
+ *     // Include a hash for each field.
+ *     result = 31 * result + (booleanField ? 1 : 0);
+ *
+ *     result = 31 * result + byteField;
+ *     result = 31 * result + charField;
+ *     result = 31 * result + shortField;
+ *     result = 31 * result + intField;
+ *
+ *     result = 31 * result + (int) (longField ^ (longField >>> 32));
+ *
+ *     result = 31 * result + Float.floatToIntBits(floatField);
+ *
+ *     long doubleFieldBits = Double.doubleToLongBits(doubleField);
+ *     result = 31 * result + (int) (doubleFieldBits ^ (doubleFieldBits >>> 32));
+ *
+ *     result = 31 * result + Arrays.hashCode(arrayField);
+ *
+ *     result = 31 * result + referenceField.hashCode();
+ *     result = 31 * result +
+ *         (nullableReferenceField == null ? 0
+ *                                         : nullableReferenceField.hashCode());
+ *
+ *     return result;
+ *   }
+ * 
+ * + *

If you don't intend your type to be used as a hash key, don't simply rely on the default + * {@code hashCode} implementation, because that silently and non-obviously breaks any future + * code that does use your type as a hash key. You should throw instead: + *

+ *   @Override public int hashCode() {
+ *     throw new UnsupportedOperationException();
+ *   }
+ * 
+ * + *

See Effective Java item 9 for much more detail and clarification. + * + *

Writing a useful {@code toString} method

+ *

For debugging convenience, it's common to override {@code toString} in this style: + *

+ *   @Override public String toString() {
+ *     return getClass().getName() + "[" +
+ *         "primitiveField=" + primitiveField + ", " +
+ *         "referenceField=" + referenceField + ", " +
+ *         "arrayField=" + Arrays.toString(arrayField) + "]";
+ *   }
+ * 
+ *

The set of fields to include is generally the same as those that would be tested + * in your {@code equals} implementation. + *

See Effective Java item 10 for much more detail and clarification. + */ +public class Object { + + private transient Class shadow$_klass_; + private transient int shadow$_monitor_; + + /** + * Constructs a new instance of {@code Object}. + */ + public Object() { + if (shadow$_klass_.isFinalizable()) { + java.lang.ref.FinalizerReference.add(this); + } + } + + /** + * Creates and returns a copy of this {@code Object}. The default + * implementation returns a so-called "shallow" copy: It creates a new + * instance of the same class and then copies the field values (including + * object references) from this instance to the new instance. A "deep" copy, + * in contrast, would also recursively clone nested objects. A subclass that + * needs to implement this kind of cloning should call {@code super.clone()} + * to create the new instance and then create deep copies of the nested, + * mutable objects. + * + * @return a copy of this object. + * @throws CloneNotSupportedException + * if this object's class does not implement the {@code + * Cloneable} interface. + */ + protected Object clone() throws CloneNotSupportedException { + if (!(this instanceof Cloneable)) { + throw new CloneNotSupportedException("Class " + getClass().getName() + + " doesn't implement Cloneable"); + } + + return internalClone(); + } + + /* + * Native helper method for cloning. + */ + private native Object internalClone(); + + /** + * Compares this instance with the specified object and indicates if they + * are equal. In order to be equal, {@code o} must represent the same object + * as this instance using a class-specific comparison. The general contract + * is that this comparison should be reflexive, symmetric, and transitive. + * Also, no object reference other than null is equal to null. + * + *

The default implementation returns {@code true} only if {@code this == + * o}. See Writing a correct + * {@code equals} method + * if you intend implementing your own {@code equals} method. + * + *

The general contract for the {@code equals} and {@link + * #hashCode()} methods is that if {@code equals} returns {@code true} for + * any two objects, then {@code hashCode()} must return the same value for + * these objects. This means that subclasses of {@code Object} usually + * override either both methods or neither of them. + * + * @param o + * the object to compare this instance with. + * @return {@code true} if the specified object is equal to this {@code + * Object}; {@code false} otherwise. + * @see #hashCode + */ + public boolean equals(Object o) { + return this == o; + } + + /** + * Invoked when the garbage collector has detected that this instance is no longer reachable. + * The default implementation does nothing, but this method can be overridden to free resources. + * + *

Note that objects that override {@code finalize} are significantly more expensive than + * objects that don't. Finalizers may be run a long time after the object is no longer + * reachable, depending on memory pressure, so it's a bad idea to rely on them for cleanup. + * Note also that finalizers are run on a single VM-wide finalizer thread, + * so doing blocking work in a finalizer is a bad idea. A finalizer is usually only necessary + * for a class that has a native peer and needs to call a native method to destroy that peer. + * Even then, it's better to provide an explicit {@code close} method (and implement + * {@link java.io.Closeable}), and insist that callers manually dispose of instances. This + * works well for something like files, but less well for something like a {@code BigInteger} + * where typical calling code would have to deal with lots of temporaries. Unfortunately, + * code that creates lots of temporaries is the worst kind of code from the point of view of + * the single finalizer thread. + * + *

If you must use finalizers, consider at least providing your own + * {@link java.lang.ref.ReferenceQueue} and having your own thread process that queue. + * + *

Unlike constructors, finalizers are not automatically chained. You are responsible for + * calling {@code super.finalize()} yourself. + * + *

Uncaught exceptions thrown by finalizers are ignored and do not terminate the finalizer + * thread. + * + * See Effective Java Item 7, "Avoid finalizers" for more. + */ + @FindBugsSuppressWarnings("FI_EMPTY") + protected void finalize() throws Throwable { + } + + /** + * Returns the unique instance of {@link Class} that represents this + * object's class. Note that {@code getClass()} is a special case in that it + * actually returns {@code Class} where {@code Foo} is the + * erasure of the type of the expression {@code getClass()} was called upon. + *

+ * As an example, the following code actually compiles, although one might + * think it shouldn't: + *

+ *

{@code
+     *   List l = new ArrayList();
+     *   Class c = l.getClass();}
+ * + * @return this object's {@code Class} instance. + */ + public final Class getClass() { + return shadow$_klass_; + } + + /** + * Returns an integer hash code for this object. By contract, any two + * objects for which {@link #equals} returns {@code true} must return + * the same hash code value. This means that subclasses of {@code Object} + * usually override both methods or neither method. + * + *

Note that hash values must not change over time unless information used in equals + * comparisons also changes. + * + *

See Writing a correct + * {@code hashCode} method + * if you intend implementing your own {@code hashCode} method. + * + * @return this object's hash code. + * @see #equals + */ + public int hashCode() { + return System.identityHashCode(this); + } + + /** + * Causes a thread which is waiting on this object's monitor (by means of + * calling one of the {@code wait()} methods) to be woken up. If more than + * one thread is waiting, one of them is chosen at the discretion of the + * VM. The chosen thread will not run immediately. The thread + * that called {@code notify()} has to release the object's monitor first. + * Also, the chosen thread still has to compete against other threads that + * try to synchronize on the same object. + *

+ * This method can only be invoked by a thread which owns this object's + * monitor. A thread becomes owner of an object's monitor + *

+ *
    + *
  • by executing a synchronized method of that object;
  • + *
  • by executing the body of a {@code synchronized} statement that + * synchronizes on the object;
  • + *
  • by executing a synchronized static method if the object is of type + * {@code Class}.
  • + *
+ * + * @see #notifyAll + * @see #wait() + * @see #wait(long) + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final native void notify(); + + /** + * Causes all threads which are waiting on this object's monitor (by means + * of calling one of the {@code wait()} methods) to be woken up. The threads + * will not run immediately. The thread that called {@code notify()} has to + * release the object's monitor first. Also, the threads still have to + * compete against other threads that try to synchronize on the same object. + *

+ * This method can only be invoked by a thread which owns this object's + * monitor. A thread becomes owner of an object's monitor + *

+ *
    + *
  • by executing a synchronized method of that object;
  • + *
  • by executing the body of a {@code synchronized} statement that + * synchronizes on the object;
  • + *
  • by executing a synchronized static method if the object is of type + * {@code Class}.
  • + *
+ * + * @throws IllegalMonitorStateException + * if the thread calling this method is not the owner of this + * object's monitor. + * @see #notify + * @see #wait() + * @see #wait(long) + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final native void notifyAll(); + + /** + * Returns a string containing a concise, human-readable description of this + * object. Subclasses are encouraged to override this method and provide an + * implementation that takes into account the object's type and data. The + * default implementation is equivalent to the following expression: + *
+     *   getClass().getName() + '@' + Integer.toHexString(hashCode())
+ *

See Writing a useful + * {@code toString} method + * if you intend implementing your own {@code toString} method. + * + * @return a printable representation of this object. + */ + public String toString() { + return getClass().getName() + '@' + Integer.toHexString(hashCode()); + } + + /** + * Causes the calling thread to wait until another thread calls the {@code + * notify()} or {@code notifyAll()} method of this object. This method can + * only be invoked by a thread which owns this object's monitor; see + * {@link #notify()} on how a thread can become the owner of a monitor. + *

+ * A waiting thread can be sent {@code interrupt()} to cause it to + * prematurely stop waiting, so {@code wait} should be called in a loop to + * check that the condition that has been waited for has been met before + * continuing. + *

+ *

+ * While the thread waits, it gives up ownership of this object's monitor. + * When it is notified (or interrupted), it re-acquires the monitor before + * it starts running. + *

+ * + * @throws IllegalMonitorStateException + * if the thread calling this method is not the owner of this + * object's monitor. + * @throws InterruptedException + * if another thread interrupts this thread while it is waiting. + * @see #notify + * @see #notifyAll + * @see #wait(long) + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final native void wait() throws InterruptedException; + + /** + * Causes the calling thread to wait until another thread calls the {@code + * notify()} or {@code notifyAll()} method of this object or until the + * specified timeout expires. This method can only be invoked by a thread + * which owns this object's monitor; see {@link #notify()} on how a thread + * can become the owner of a monitor. + *

+ * A waiting thread can be sent {@code interrupt()} to cause it to + * prematurely stop waiting, so {@code wait} should be called in a loop to + * check that the condition that has been waited for has been met before + * continuing. + *

+ *

+ * While the thread waits, it gives up ownership of this object's monitor. + * When it is notified (or interrupted), it re-acquires the monitor before + * it starts running. + *

+ * + * @param millis + * the maximum time to wait in milliseconds. + * @throws IllegalArgumentException + * if {@code millis < 0}. + * @throws IllegalMonitorStateException + * if the thread calling this method is not the owner of this + * object's monitor. + * @throws InterruptedException + * if another thread interrupts this thread while it is waiting. + * @see #notify + * @see #notifyAll + * @see #wait() + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final void wait(long millis) throws InterruptedException { + wait(millis, 0); + } + + /** + * Causes the calling thread to wait until another thread calls the {@code + * notify()} or {@code notifyAll()} method of this object or until the + * specified timeout expires. This method can only be invoked by a thread + * that owns this object's monitor; see {@link #notify()} on how a thread + * can become the owner of a monitor. + *

+ * A waiting thread can be sent {@code interrupt()} to cause it to + * prematurely stop waiting, so {@code wait} should be called in a loop to + * check that the condition that has been waited for has been met before + * continuing. + *

+ *

+ * While the thread waits, it gives up ownership of this object's monitor. + * When it is notified (or interrupted), it re-acquires the monitor before + * it starts running. + *

+ * + * @param millis + * the maximum time to wait in milliseconds. + * @param nanos + * the fraction of a millisecond to wait, specified in + * nanoseconds. + * @throws IllegalArgumentException + * if {@code millis < 0}, {@code nanos < 0} or {@code nanos > + * 999999}. + * @throws IllegalMonitorStateException + * if the thread calling this method is not the owner of this + * object's monitor. + * @throws InterruptedException + * if another thread interrupts this thread while it is waiting. + * @see #notify + * @see #notifyAll + * @see #wait() + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final native void wait(long millis, int nanos) throws InterruptedException; +} diff --git a/libart/src/main/java/java/lang/Thread.java b/libart/src/main/java/java/lang/Thread.java new file mode 100644 index 0000000..b9d7462 --- /dev/null +++ b/libart/src/main/java/java/lang/Thread.java @@ -0,0 +1,1254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/* + * Copyright (C) 2008 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 java.lang; + +import dalvik.system.VMStack; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import libcore.util.EmptyArray; + +/** + * A {@code Thread} is a concurrent unit of execution. It has its own call stack + * for methods being invoked, their arguments and local variables. Each application + * has at least one thread running when it is started, the main thread, in the main + * {@link ThreadGroup}. The runtime keeps its own threads in the system thread + * group. + * + *

There are two ways to execute code in a new thread. + * You can either subclass {@code Thread} and overriding its {@link #run()} method, + * or construct a new {@code Thread} and pass a {@link Runnable} to the constructor. + * In either case, the {@link #start()} method must be called to actually execute + * the new {@code Thread}. + * + *

Each {@code Thread} has an integer priority that affect how the thread is + * scheduled by the OS. A new thread inherits the priority of its parent. + * A thread's priority can be set using the {@link #setPriority(int)} method. + */ +public class Thread implements Runnable { + private static final int NANOS_PER_MILLI = 1000000; + + /** Park states */ + private static class ParkState { + /** park state indicating unparked */ + private static final int UNPARKED = 1; + + /** park state indicating preemptively unparked */ + private static final int PREEMPTIVELY_UNPARKED = 2; + + /** park state indicating parked */ + private static final int PARKED = 3; + } + + /** + * A representation of a thread's state. A given thread may only be in one + * state at a time. + */ + public enum State { + /** + * The thread has been created, but has never been started. + */ + NEW, + /** + * The thread may be run. + */ + RUNNABLE, + /** + * The thread is blocked and waiting for a lock. + */ + BLOCKED, + /** + * The thread is waiting. + */ + WAITING, + /** + * The thread is waiting for a specified amount of time. + */ + TIMED_WAITING, + /** + * The thread has been terminated. + */ + TERMINATED + } + + /** + * The maximum priority value allowed for a thread. + * This corresponds to (but does not have the same value as) + * {@code android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY}. + */ + public static final int MAX_PRIORITY = 10; + + /** + * The minimum priority value allowed for a thread. + * This corresponds to (but does not have the same value as) + * {@code android.os.Process.THREAD_PRIORITY_LOWEST}. + */ + public static final int MIN_PRIORITY = 1; + + /** + * The normal (default) priority value assigned to the main thread. + * This corresponds to (but does not have the same value as) + * {@code android.os.Process.THREAD_PRIORITY_DEFAULT}. + + */ + public static final int NORM_PRIORITY = 5; + + /* some of these are accessed directly by the VM; do not rename them */ + private volatile int vmData; + volatile ThreadGroup group; + volatile boolean daemon; + volatile String name; + volatile int priority; + volatile long stackSize; + Runnable target; + private static int count = 0; + + /** + * Holds the thread's ID. We simply count upwards, so + * each Thread has a unique ID. + */ + private long id; + + /** + * Normal thread local values. + */ + ThreadLocal.Values localValues; + + /** + * Inheritable thread local values. + */ + ThreadLocal.Values inheritableValues; + + /** Callbacks to run on interruption. */ + private final List interruptActions = new ArrayList(); + + /** + * Holds the class loader for this Thread, in case there is one. + */ + private ClassLoader contextClassLoader; + + /** + * Holds the handler for uncaught exceptions in this Thread, + * in case there is one. + */ + private UncaughtExceptionHandler uncaughtHandler; + + /** + * Holds the default handler for uncaught exceptions, in case there is one. + */ + private static UncaughtExceptionHandler defaultUncaughtHandler; + + /** + * Reflects whether this Thread has already been started. A Thread + * can only be started once (no recycling). Also, we need it to deduce + * the proper Thread status. + */ + boolean hasBeenStarted = false; + + /** the park state of the thread */ + private int parkState = ParkState.UNPARKED; + + /** + * The synchronization object responsible for this thread's join/sleep/park operations. + */ + private final Object lock = new Object(); + + /** Looked up reflectively and used by java.util.concurrent.locks.LockSupport. */ + private Object parkBlocker; + + /** + * Constructs a new {@code Thread} with no {@code Runnable} object and a + * newly generated name. The new {@code Thread} will belong to the same + * {@code ThreadGroup} as the {@code Thread} calling this constructor. + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread() { + create(null, null, null, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object and a + * newly generated name. The new {@code Thread} will belong to the same + * {@code ThreadGroup} as the {@code Thread} calling this constructor. + * + * @param runnable + * a {@code Runnable} whose method run will be + * executed by the new {@code Thread} + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(Runnable runnable) { + create(null, runnable, null, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object and name + * provided. The new {@code Thread} will belong to the same {@code + * ThreadGroup} as the {@code Thread} calling this constructor. + * + * @param runnable + * a {@code Runnable} whose method run will be + * executed by the new {@code Thread} + * @param threadName + * the name for the {@code Thread} being created + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(Runnable runnable, String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(null, runnable, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with no {@code Runnable} object and the + * name provided. The new {@code Thread} will belong to the same {@code + * ThreadGroup} as the {@code Thread} calling this constructor. + * + * @param threadName + * the name for the {@code Thread} being created + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + * + */ + public Thread(String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(null, null, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object and a + * newly generated name. The new {@code Thread} will belong to the {@code + * ThreadGroup} passed as parameter. + * + * @param group + * {@code ThreadGroup} to which the new {@code Thread} will + * belong + * @param runnable + * a {@code Runnable} whose method run will be + * executed by the new {@code Thread} + * @throws IllegalThreadStateException + * if group.destroy() has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, Runnable runnable) { + create(group, runnable, null, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object, the given + * name and belonging to the {@code ThreadGroup} passed as parameter. + * + * @param group + * ThreadGroup to which the new {@code Thread} will belong + * @param runnable + * a {@code Runnable} whose method run will be + * executed by the new {@code Thread} + * @param threadName + * the name for the {@code Thread} being created + * @throws IllegalThreadStateException + * if group.destroy() has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, Runnable runnable, String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(group, runnable, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with no {@code Runnable} object, the + * given name and belonging to the {@code ThreadGroup} passed as parameter. + * + * @param group + * {@code ThreadGroup} to which the new {@code Thread} will belong + * @param threadName + * the name for the {@code Thread} being created + * @throws IllegalThreadStateException + * if group.destroy() has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(group, null, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object, the given + * name and belonging to the {@code ThreadGroup} passed as parameter. + * + * @param group + * {@code ThreadGroup} to which the new {@code Thread} will + * belong + * @param runnable + * a {@code Runnable} whose method run will be + * executed by the new {@code Thread} + * @param threadName + * the name for the {@code Thread} being created + * @param stackSize + * a stack size for the new {@code Thread}. This has a highly + * platform-dependent interpretation. It may even be ignored + * completely. + * @throws IllegalThreadStateException + * if group.destroy() has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + create(group, runnable, threadName, stackSize); + } + + /** + * Package-scope method invoked by Dalvik VM to create "internal" + * threads or attach threads created externally. + * + * Don't call Thread.currentThread(), since there may not be such + * a thing (e.g. for Main). + */ + Thread(ThreadGroup group, String name, int priority, boolean daemon) { + synchronized (Thread.class) { + id = ++Thread.count; + } + + if (name == null) { + this.name = "Thread-" + id; + } else { + this.name = name; + } + + if (group == null) { + throw new InternalError("group == null"); + } + + this.group = group; + + this.target = null; + this.stackSize = 0; + this.priority = priority; + this.daemon = daemon; + + /* add ourselves to our ThreadGroup of choice */ + this.group.addThread(this); + } + + /** + * Initializes a new, existing Thread object with a runnable object, + * the given name and belonging to the ThreadGroup passed as parameter. + * This is the method that the several public constructors delegate their + * work to. + * + * @param group ThreadGroup to which the new Thread will belong + * @param runnable a java.lang.Runnable whose method run will + * be executed by the new Thread + * @param threadName Name for the Thread being created + * @param stackSize Platform dependent stack size + * @throws IllegalThreadStateException if group.destroy() has + * already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { + Thread currentThread = Thread.currentThread(); + if (group == null) { + group = currentThread.getThreadGroup(); + } + + if (group.isDestroyed()) { + throw new IllegalThreadStateException("Group already destroyed"); + } + + this.group = group; + + synchronized (Thread.class) { + id = ++Thread.count; + } + + if (threadName == null) { + this.name = "Thread-" + id; + } else { + this.name = threadName; + } + + this.target = runnable; + this.stackSize = stackSize; + + this.priority = currentThread.getPriority(); + + this.contextClassLoader = currentThread.contextClassLoader; + + // Transfer over InheritableThreadLocals. + if (currentThread.inheritableValues != null) { + inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues); + } + + // add ourselves to our ThreadGroup of choice + this.group.addThread(this); + } + + /** + * Returns the number of active {@code Thread}s in the running {@code + * Thread}'s group and its subgroups. + * + * @return the number of {@code Thread}s + */ + public static int activeCount() { + return currentThread().getThreadGroup().activeCount(); + } + + /** + * Does nothing. + */ + public final void checkAccess() { + } + + /** + * Returns the number of stack frames in this thread. + * + * @return Number of stack frames + * @deprecated The results of this call were never well defined. To make + * things worse, it would depend on whether the Thread was + * suspended or not, and suspend was deprecated too. + */ + @Deprecated + public int countStackFrames() { + return getStackTrace().length; + } + + /** + * Returns the Thread of the caller, that is, the current Thread. + */ + public static native Thread currentThread(); + + /** + * Throws {@code UnsupportedOperationException}. + * @deprecated Not implemented. + */ + @Deprecated + public void destroy() { + throw new UnsupportedOperationException(); + } + + /** + * Prints to the standard error stream a text representation of the current + * stack for this Thread. + * + * @see Throwable#printStackTrace() + */ + public static void dumpStack() { + new Throwable("stack dump").printStackTrace(); + } + + /** + * Copies an array with all Threads which are in the same ThreadGroup as the + * receiver - and subgroups - into the array threads passed as + * parameter. If the array passed as parameter is too small no exception is + * thrown - the extra elements are simply not copied. + * + * @param threads + * array into which the Threads will be copied + * @return How many Threads were copied over + */ + public static int enumerate(Thread[] threads) { + Thread thread = Thread.currentThread(); + return thread.getThreadGroup().enumerate(threads); + } + + /** + * Returns a map of all the currently live threads to their stack traces. + */ + public static Map getAllStackTraces() { + Map map = new HashMap(); + + // Find out how many live threads we have. Allocate a bit more + // space than needed, in case new ones are just being created. + int count = ThreadGroup.systemThreadGroup.activeCount(); + Thread[] threads = new Thread[count + count / 2]; + + // Enumerate the threads and collect the stacktraces. + count = ThreadGroup.systemThreadGroup.enumerate(threads); + for (int i = 0; i < count; i++) { + map.put(threads[i], threads[i].getStackTrace()); + } + + return map; + } + + /** + * Returns the context ClassLoader for this Thread. + * + * @return ClassLoader The context ClassLoader + * @see java.lang.ClassLoader + * @see #getContextClassLoader() + */ + public ClassLoader getContextClassLoader() { + return contextClassLoader; + } + + /** + * Returns the default exception handler that's executed when uncaught + * exception terminates a thread. + * + * @return an {@link UncaughtExceptionHandler} or null if + * none exists. + */ + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return defaultUncaughtHandler; + } + + /** + * Returns the thread's identifier. The ID is a positive long + * generated on thread creation, is unique to the thread, and doesn't change + * during the lifetime of the thread; the ID may be reused after the thread + * has been terminated. + * + * @return the thread's ID. + */ + public long getId() { + return id; + } + + /** + * Returns the name of the Thread. + */ + public final String getName() { + return name; + } + + /** + * Returns the priority of the Thread. + */ + public final int getPriority() { + return priority; + } + + /** + * Returns an array of {@link StackTraceElement} representing the current thread's stack. + */ + public StackTraceElement[] getStackTrace() { + StackTraceElement ste[] = VMStack.getThreadStackTrace(this); + return ste != null ? ste : EmptyArray.STACK_TRACE_ELEMENT; + } + + /** + * Returns the current state of the Thread. This method is useful for + * monitoring purposes. + * + * @return a {@link State} value. + */ + public State getState() { + return State.values()[nativeGetStatus(hasBeenStarted)]; + } + + private native int nativeGetStatus(boolean hasBeenStarted); + + /** + * Returns the ThreadGroup to which this Thread belongs. + * + * @return the Thread's ThreadGroup + */ + public final ThreadGroup getThreadGroup() { + // TODO This should actually be done at native termination. + if (getState() == Thread.State.TERMINATED) { + return null; + } else { + return group; + } + } + + /** + * Returns the thread's uncaught exception handler. If not explicitly set, + * then the ThreadGroup's handler is returned. If the thread is terminated, + * then null is returned. + * + * @return an {@link UncaughtExceptionHandler} instance or {@code null}. + */ + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + if (uncaughtHandler != null) { + return uncaughtHandler; + } else { + return group; // ThreadGroup is instance of UEH + } + } + + /** + * Posts an interrupt request to this {@code Thread}. The behavior depends on + * the state of this {@code Thread}: + *