summaryrefslogtreecommitdiffstats
path: root/libdvm/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'libdvm/src/main')
-rw-r--r--libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java132
-rw-r--r--libdvm/src/main/java/dalvik/system/DexFile.java296
-rw-r--r--libdvm/src/main/java/dalvik/system/DexPathList.java473
-rw-r--r--libdvm/src/main/java/java/lang/Class.java1218
-rw-r--r--libdvm/src/main/java/java/lang/ClassLoader.java836
-rw-r--r--libdvm/src/main/java/java/lang/Daemons.java281
-rw-r--r--libdvm/src/main/java/java/lang/Object.java442
-rw-r--r--libdvm/src/main/java/java/lang/Thread.java1281
-rw-r--r--libdvm/src/main/java/java/lang/ThreadGroup.java726
-rw-r--r--libdvm/src/main/java/java/lang/VMClassLoader.java85
-rw-r--r--libdvm/src/main/java/java/lang/VMThread.java77
-rw-r--r--libdvm/src/main/java/java/lang/reflect/AccessibleObject.java320
-rw-r--r--libdvm/src/main/java/java/lang/reflect/Constructor.java460
-rw-r--r--libdvm/src/main/java/java/lang/reflect/Field.java934
-rw-r--r--libdvm/src/main/java/java/lang/reflect/Method.java597
-rw-r--r--libdvm/src/main/java/java/lang/reflect/Proxy.java278
-rw-r--r--libdvm/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java63
-rw-r--r--libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java317
-rw-r--r--libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java387
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/GenericSignatureParser.java508
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForArray.java40
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForType.java88
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForVariable.java139
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForWildcard.java70
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfTypes.java79
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfVariables.java35
-rw-r--r--libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/Types.java34
-rw-r--r--libdvm/src/main/java/sun/misc/Unsafe.java350
28 files changed, 10546 insertions, 0 deletions
diff --git a/libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java b/libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java
new file mode 100644
index 0000000..d3ec95a
--- /dev/null
+++ b/libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -0,0 +1,132 @@
+/*
+ * 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.Enumeration;
+
+/**
+ * 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 {
+ Class c = pathList.findClass(name);
+ if (c == null) {
+ throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
+ }
+ return c;
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return pathList.findResource(name);
+ }
+
+ @Override
+ protected Enumeration<URL> 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.
+ *
+ * <p>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/libdvm/src/main/java/dalvik/system/DexFile.java b/libdvm/src/main/java/dalvik/system/DexFile.java
new file mode 100644
index 0000000..18b730b
--- /dev/null
+++ b/libdvm/src/main/java/dalvik/system/DexFile.java
@@ -0,0 +1,296 @@
+/*
+ * 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.Enumeration;
+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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 {
+ guard.close();
+ closeDexFile(mCookie);
+ mCookie = 0;
+ }
+
+ /**
+ * Loads a class. Returns the class on success, or a {@code null} reference
+ * on failure.
+ * <p>
+ * 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.
+ * <p>
+ * 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);
+ }
+
+ /**
+ * See {@link #loadClass(String, ClassLoader)}.
+ *
+ * This takes a "binary" class name to better match ClassLoader semantics.
+ *
+ * @hide
+ */
+ public Class loadClassBinaryName(String name, ClassLoader loader) {
+ return defineClass(name, loader, mCookie);
+ }
+
+ private native static Class defineClass(String name, ClassLoader loader, int cookie);
+
+ /**
+ * 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<String> entries() {
+ return new DFEnum(this);
+ }
+
+ /*
+ * Helper class.
+ */
+ private class DFEnum implements Enumeration<String> {
+ 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/libdvm/src/main/java/dalvik/system/DexPathList.java b/libdvm/src/main/java/dalvik/system/DexPathList.java
new file mode 100644
index 0000000..3d9ee3e
--- /dev/null
+++ b/libdvm/src/main/java/dalvik/system/DexPathList.java
@@ -0,0 +1,473 @@
+/*
+ * 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.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 &mdash; typically referred
+ * to as a "class path" &mdash; 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).
+ *
+ * <p>This class also contains methods to use these lists to look up
+ * classes and resources.</p>
+ */
+/*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<File> 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<File> 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<File> splitPaths(String path1, String path2,
+ boolean wantDirectories) {
+ ArrayList<File> result = new ArrayList<File>();
+
+ 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<File> 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<File> files,
+ File optimizedDirectory) {
+ ArrayList<Element> elements = new ArrayList<Element>();
+
+ /*
+ * 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 optimized dex file.
+ */
+ private static String optimizedPathFor(File path,
+ File optimizedDirectory) {
+ /*
+ * Get the filename component of the path, and replace the
+ * suffix with ".dex" if that's not already the suffix.
+ *
+ * We don't want to use ".odex", because the build system uses
+ * that for files that are paired with resource-only jar
+ * files. If the VM can assume that there's no classes.dex in
+ * the matching jar, it doesn't need to open the jar to check
+ * for updated dependencies, providing a slight performance
+ * boost at startup. The use of ".dex" here matches the use on
+ * files in /data/dalvik-cache.
+ */
+ String fileName = path.getName();
+ if (!fileName.endsWith(DEX_SUFFIX)) {
+ int lastDot = fileName.lastIndexOf(".");
+ if (lastDot < 0) {
+ fileName += DEX_SUFFIX;
+ } else {
+ StringBuilder sb = new StringBuilder(lastDot + 4);
+ sb.append(fileName, 0, lastDot);
+ sb.append(DEX_SUFFIX);
+ fileName = sb.toString();
+ }
+ }
+
+ 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.
+ *
+ * @return the named class or {@code null} if the class is not
+ * found in any of the dex files
+ */
+ public Class findClass(String name) {
+ for (Element element : dexElements) {
+ DexFile dex = element.dexFile;
+
+ if (dex != null) {
+ Class clazz = dex.loadClassBinaryName(name, definingContext);
+ 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<URL> findResources(String name) {
+ ArrayList<URL> result = new ArrayList<URL>();
+
+ 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/libdvm/src/main/java/java/lang/Class.java b/libdvm/src/main/java/java/lang/Class.java
new file mode 100644
index 0000000..f618b66
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/Class.java
@@ -0,0 +1,1218 @@
+/*
+ * 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 dalvik.system.VMStack;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericDeclaration;
+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.Collection;
+import java.util.HashMap;
+import java.util.List;
+import libcore.util.CollectionUtils;
+import libcore.util.EmptyArray;
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.Types;
+
+/**
+ * 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.
+ *
+ * <h4>Class instances representing object types (classes or interfaces)</h4>
+ * <p>
+ * 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 <em>signature</em>, which is the letter "L", followed by the
+ * class name and a semicolon (";"). The signature is what the runtime system
+ * uses internally for identifying the class (for example in a DEX file).
+ * </p>
+ * <h4>Classes representing primitive types</h4>
+ * <p>
+ * 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 signatures are:
+ * </p>
+ * <ul>
+ * <li>{@code B} representing the {@code byte} primitive type</li>
+ * <li>{@code S} representing the {@code short} primitive type</li>
+ * <li>{@code I} representing the {@code int} primitive type</li>
+ * <li>{@code J} representing the {@code long} primitive type</li>
+ * <li>{@code F} representing the {@code float} primitive type</li>
+ * <li>{@code D} representing the {@code double} primitive type</li>
+ * <li>{@code C} representing the {@code char} primitive type</li>
+ * <li>{@code Z} representing the {@code boolean} primitive type</li>
+ * <li>{@code V} representing void function return values</li>
+ * </ul>
+ * <p>
+ * <h4>Classes representing array classes</h4>
+ * <p>
+ * 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 signature of the class representing the leaf component type,
+ * which can be either an object type or a primitive type. The signature of a
+ * {@code Class} representing an array type is the same as its name. Examples
+ * of array class signatures are:
+ * </p>
+ * <ul>
+ * <li>{@code [I} representing the {@code int[]} type</li>
+ * <li>{@code [Ljava/lang/String;} representing the {@code String[]} type</li>
+ * <li>{@code [[[C} representing the {@code char[][][]} type (three dimensions!)</li>
+ * </ul>
+ */
+public final class Class<T> implements Serializable, AnnotatedElement, GenericDeclaration, Type {
+
+ private static final long serialVersionUID = 3206093459760846163L;
+
+ /**
+ * Lazily computed name of this class; always prefer calling getName().
+ */
+ private transient String name;
+
+ private Class() {
+ // Prevent this class to be instantiated, instance
+ // should be created by JVM only
+ }
+
+ /**
+ * Get the Signature attribute for this class. Returns null if not found.
+ */
+ private String getSignatureAttribute() {
+ Object[] annotation = getSignatureAnnotation();
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Get the Signature annotation for this class. Returns null if not found.
+ */
+ native private Object[] getSignatureAnnotation();
+
+ /**
+ * 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.
+ *
+ * <p>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 can not 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.
+ *
+ * <p>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 can not 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 ExceptionInInitializerError) {
+ throw (ExceptionInInitializerError) cause;
+ }
+ throw e;
+ }
+ return result;
+ }
+
+ private static native Class<?> classForName(String className, boolean shouldInitialize,
+ ClassLoader classLoader) throws ClassNotFoundException;
+
+ /**
+ * Returns an array containing {@code Class} objects for all public classes
+ * and interfaces that are members of this class. This includes public
+ * members inherited from super classes and 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() {
+ Class<?>[] result = getDeclaredClasses(this, true);
+ // Traverse all superclasses.
+ for (Class<?> c = this.getSuperclass(); c != null; c = c.getSuperclass()) {
+ Class<?>[] temp = getDeclaredClasses(c, true);
+ if (temp.length != 0) {
+ result = arraycopy(new Class[result.length + temp.length], result, temp);
+ }
+ }
+ return result;
+ }
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+
+ A annotation = getDeclaredAnnotation(annotationType);
+ if (annotation != null) {
+ return annotation;
+ }
+
+ if (annotationType.isAnnotationPresent(Inherited.class)) {
+ for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
+ annotation = sup.getDeclaredAnnotation(annotationType);
+ if (annotation != null) {
+ return annotation;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns an array containing all the annotations of this class. If there are no annotations
+ * then an empty array is returned.
+ *
+ * @see #getDeclaredAnnotations()
+ */
+ public Annotation[] getAnnotations() {
+ /*
+ * We need to get the annotations declared on this class, plus the
+ * annotations from superclasses that have the "@Inherited" annotation
+ * set. We create a temporary map to use while we accumulate the
+ * annotations and convert it to an array at the end.
+ *
+ * It's possible to have duplicates when annotations are inherited.
+ * We use a Map to filter those out.
+ *
+ * HashMap might be overkill here.
+ */
+ HashMap<Class, Annotation> map = new HashMap<Class, Annotation>();
+ Annotation[] declaredAnnotations = getDeclaredAnnotations();
+
+ for (int i = declaredAnnotations.length-1; i >= 0; --i) {
+ map.put(declaredAnnotations[i].annotationType(), declaredAnnotations[i]);
+ }
+ for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
+ declaredAnnotations = sup.getDeclaredAnnotations();
+ for (int i = declaredAnnotations.length-1; i >= 0; --i) {
+ Class<?> clazz = declaredAnnotations[i].annotationType();
+ if (!map.containsKey(clazz) && clazz.isAnnotationPresent(Inherited.class)) {
+ map.put(clazz, declaredAnnotations[i]);
+ }
+ }
+ }
+
+ /* convert annotation values from HashMap to array */
+ Collection<Annotation> coll = map.values();
+ return coll.toArray(new Annotation[coll.size()]);
+ }
+
+ /**
+ * 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 = getClassLoader(this);
+ return loader == null ? BootClassLoader.getInstance() : loader;
+ }
+
+ /*
+ * Returns the defining class loader for the given class.
+ */
+ private static native ClassLoader getClassLoader(Class<?> c);
+
+ /**
+ * 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 native Class<?> getComponentType();
+
+ /**
+ * 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 can not be found.
+ * @see #getDeclaredConstructor(Class[])
+ */
+ @SuppressWarnings("unchecked")
+ public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException {
+ return (Constructor) getConstructorOrMethod("<init>", false, true, parameterTypes);
+ }
+
+ /**
+ * Returns a constructor or method with the given name. Use "<init>" to return a constructor.
+ */
+ private Member getConstructorOrMethod(String name, boolean searchSuperTypes,
+ boolean publicOnly, Class<?>[] parameterTypes) throws NoSuchMethodException {
+ if (searchSuperTypes && !publicOnly) {
+ throw new AssertionError(); // can't lookup non-public members recursively
+ }
+ 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");
+ }
+ }
+ Member result = searchSuperTypes
+ ? getPublicConstructorOrMethodRecursive(name, parameterTypes)
+ : Class.getDeclaredConstructorOrMethod(this, name, parameterTypes);
+ if (result == null || publicOnly && (result.getModifiers() & Modifier.PUBLIC) == 0) {
+ throw new NoSuchMethodException(name + " " + Arrays.toString(parameterTypes));
+ }
+ return result;
+ }
+
+ private Member getPublicConstructorOrMethodRecursive(String name, Class<?>[] parameterTypes) {
+ // search superclasses
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ Member result = Class.getDeclaredConstructorOrMethod(c, name, parameterTypes);
+ if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
+ return result;
+ }
+ }
+
+ // search implemented interfaces
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ for (Class<?> ifc : c.getInterfaces()) {
+ Member result = ifc.getPublicConstructorOrMethodRecursive(name, parameterTypes);
+ if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
+ return result;
+ }
+ }
+ }
+
+ 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() {
+ return getDeclaredConstructors(this, true);
+ }
+
+ /**
+ * 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()
+ */
+ public native Annotation[] getDeclaredAnnotations();
+
+ /**
+ * Returns the annotation if it exists.
+ */
+ native private <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass);
+
+ /**
+ * Returns true if the annotation exists.
+ */
+ native private boolean isDeclaredAnnotationPresent(Class<? extends Annotation> annotationClass);
+
+ /**
+ * Returns an array containing {@code Class} objects for all classes and
+ * interfaces that are declared as members of the class which this {@code
+ * Class} represents. If there are no classes or interfaces declared or if
+ * this class represents an array class, a primitive type or void, then an
+ * empty array is returned.
+ */
+ public Class<?>[] getDeclaredClasses() {
+ return getDeclaredClasses(this, false);
+ }
+
+ /*
+ * Returns the list of member classes of the given class.
+ * If no members exist, an empty array is returned.
+ */
+ private static native Class<?>[] getDeclaredClasses(Class<?> c, boolean publicOnly);
+
+ /**
+ * Returns a {@code Constructor} object which represents the constructor
+ * matching the given 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 can not be found.
+ * @see #getConstructor(Class[])
+ */
+ @SuppressWarnings("unchecked")
+ public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
+ throws NoSuchMethodException {
+ return (Constructor) getConstructorOrMethod("<init>", false, false, parameterTypes);
+ }
+
+ /**
+ * 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() {
+ return getDeclaredConstructors(this, false);
+ }
+
+ /*
+ * Returns the list of constructors. If no constructors exist, an empty array is returned.
+ */
+ private static native <T> Constructor<T>[] getDeclaredConstructors(Class<T> c,
+ boolean publicOnly);
+
+ /**
+ * 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 = getDeclaredField(this, name);
+ if (result == null) {
+ throw new NoSuchFieldException(name);
+ }
+ 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() {
+ return getDeclaredFields(this, false);
+ }
+
+ /*
+ * Returns the list of fields without performing any security checks
+ * first. If no fields exist at all, an empty array is returned.
+ */
+ static native Field[] getDeclaredFields(Class<?> c, boolean publicOnly);
+
+ /**
+ * Returns the field if it is defined by {@code c}; null otherwise. This
+ * may return a non-public member.
+ */
+ static native Field getDeclaredField(Class<?> c, String name);
+
+ /**
+ * Returns a {@code Method} object which represents the method matching the
+ * given name and 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 method can not be found.
+ * @throws NullPointerException
+ * if {@code name} is {@code null}.
+ * @see #getMethod(String, Class[])
+ */
+ public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
+ throws NoSuchMethodException {
+ Member member = getConstructorOrMethod(name, false, false, parameterTypes);
+ if (member instanceof Constructor) {
+ throw new NoSuchMethodException(name);
+ }
+ return (Method) member;
+ }
+
+ /**
+ * 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() {
+ return getDeclaredMethods(this, false);
+ }
+
+ /**
+ * Returns the list of methods. If no methods exist, an empty array is returned.
+ */
+ static native Method[] getDeclaredMethods(Class<?> c, boolean publicOnly);
+
+ /**
+ * Returns the constructor or method if it is defined by {@code c}; null
+ * otherwise. This may return a non-public member. Use "<init>" to get a constructor.
+ */
+ static native Member getDeclaredConstructorOrMethod(Class c, String name, Class[] args);
+
+ /**
+ * Returns the declaring {@code Class} of this {@code Class}. Returns
+ * {@code null} if the class is not a member of another class or if this
+ * {@code Class} represents an array class, a primitive type, or void.
+ */
+ public native Class<?> getDeclaringClass();
+
+ /**
+ * Returns the enclosing {@code Class} of this {@code Class}. If there is no
+ * enclosing class the method returns {@code null}.
+ */
+ public native Class<?> getEnclosingClass();
+
+ /**
+ * Returns the enclosing {@code Constructor} of this {@code Class}, if it is an
+ * anonymous or local/automatic class; otherwise {@code null}.
+ */
+ public native Constructor<?> getEnclosingConstructor();
+
+ /**
+ * Returns the enclosing {@code Method} of this {@code Class}, if it is an
+ * anonymous or local/automatic class; otherwise {@code null}.
+ */
+ public native Method getEnclosingMethod();
+
+ /**
+ * 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 can not 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);
+ }
+ return result;
+ }
+
+ private Field getPublicFieldRecursive(String name) {
+ // search superclasses
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ Field result = Class.getDeclaredField(c, name);
+ if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
+ return result;
+ }
+ }
+
+ // search implemented interfaces
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ for (Class<?> ifc : c.getInterfaces()) {
+ 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.
+ *
+ * <p>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<Field> fields = new ArrayList<Field>();
+ getPublicFieldsRecursive(fields);
+
+ /*
+ * The result may include duplicates when this class implements an interface
+ * through multiple paths. Remove those duplicates.
+ */
+ CollectionUtils.removeDuplicates(fields, Field.ORDER_BY_NAME_AND_DECLARING_CLASS);
+ return fields.toArray(new Field[fields.size()]);
+ }
+
+ /**
+ * Populates {@code result} with public fields defined by this class, its
+ * superclasses, and all implemented interfaces.
+ */
+ private void getPublicFieldsRecursive(List<Field> result) {
+ // search superclasses
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ for (Field field : Class.getDeclaredFields(c, true)) {
+ result.add(field);
+ }
+ }
+
+ // search implemented interfaces
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ for (Class<?> ifc : c.getInterfaces()) {
+ ifc.getPublicFieldsRecursive(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, getSignatureAttribute());
+ 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, getSignatureAttribute());
+ 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.
+ */
+ public native Class<?>[] getInterfaces();
+
+ /**
+ * Returns a {@code Method} object which represents the public method with
+ * the given 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 can not be found.
+ * @see #getDeclaredMethod(String, Class[])
+ */
+ public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException {
+ Member member = getConstructorOrMethod(name, true, true, parameterTypes);
+ if (member instanceof Constructor) {
+ throw new NoSuchMethodException(name);
+ }
+ return (Method) member;
+ }
+
+ /**
+ * 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.
+ *
+ * <p>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<Method> methods = new ArrayList<Method>();
+ getPublicMethodsRecursive(methods);
+
+ /*
+ * Remove methods defined by multiple types, 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 getPublicMethodsRecursive(List<Method> result) {
+ // search superclasses
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ for (Method method : Class.getDeclaredMethods(c, true)) {
+ result.add(method);
+ }
+ }
+
+ // search implemented interfaces
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ for (Class<?> ifc : c.getInterfaces()) {
+ ifc.getPublicMethodsRecursive(result);
+ }
+ }
+ }
+
+ /**
+ * 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() {
+ return getModifiers(this, false);
+ }
+
+ /*
+ * Returns the modifiers for the given class.
+ *
+ * {@code ignoreInnerClassesAttrib} determines whether we look for and use the
+ * flags from an "inner class" attribute
+ */
+ private static native int getModifiers(Class<?> clazz, boolean ignoreInnerClassesAttrib);
+
+ /**
+ * 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() + "[]";
+ }
+
+ String name = getName();
+
+ if (isAnonymousClass()) {
+ return "";
+ }
+
+ if (isMemberClass() || isLocalClass()) {
+ return getInnerClassName();
+ }
+
+ 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 native String getInnerClassName();
+
+ /**
+ * 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 native Class<? super T> getSuperclass();
+
+ /**
+ * 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")
+ public synchronized TypeVariable<Class<T>>[] getTypeParameters() {
+ GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+ parser.parseForClass(this, getSignatureAttribute());
+ 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.Modifiers
+ int mod = getModifiers(this, true);
+ return (mod & ACC_ANNOTATION) != 0;
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+
+ if (isDeclaredAnnotationPresent(annotationType)) {
+ return true;
+ }
+
+ if (annotationType.isDeclaredAnnotationPresent(Inherited.class)) {
+ for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
+ if (sup.isDeclaredAnnotationPresent(annotationType)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Tests whether the class represented by this {@code Class} is
+ * anonymous.
+ */
+ native public boolean isAnonymousClass();
+
+ /**
+ * Tests whether the class represented by this {@code Class} is an array class.
+ */
+ public boolean isArray() {
+ return getComponentType() != null;
+ }
+
+ /**
+ * Tests whether the given class type can be converted to the class
+ * represented by this {@code Class}. Conversion may be done via an identity
+ * conversion or a widening reference conversion (if either the receiver or
+ * the argument represent primitive types, only the identity conversion
+ * applies).
+ *
+ * @throws NullPointerException
+ * if {@code c} is {@code null}.
+ */
+ public native boolean isAssignableFrom(Class<?> c);
+
+ /**
+ * 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 native boolean isInstance(Object object);
+
+ /**
+ * Tests whether this {@code Class} represents an interface.
+ */
+ public native boolean isInterface();
+
+ /**
+ * Tests whether the class represented by this {@code Class} is defined
+ * locally.
+ */
+ public boolean isLocalClass() {
+ boolean enclosed = (getEnclosingMethod() != null ||
+ getEnclosingConstructor() != null);
+ return enclosed && !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 native boolean isPrimitive();
+
+ /**
+ * Tests whether this {@code Class} represents a synthetic type.
+ */
+ public boolean isSynthetic() {
+ final int ACC_SYNTHETIC = 0x1000; // not public in reflect.Modifiers
+ int mod = getModifiers(this, true);
+ return (mod & ACC_SYNTHETIC) != 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 can not be created.
+ */
+ public T newInstance() throws InstantiationException, IllegalAccessException {
+ return newInstanceImpl();
+ }
+
+ private native T newInstanceImpl() throws IllegalAccessException, InstantiationException;
+
+ @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 name = getName();
+ int dot = name.lastIndexOf('.');
+ return (dot != -1 ? loader.getPackage(name.substring(0, dot)) : null);
+ }
+ return null;
+ }
+
+ /**
+ * 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 native boolean desiredAssertionStatus();
+
+ /**
+ * 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 <U> Class<? extends U> asSubclass(Class<U> c) {
+ if (c.isAssignableFrom(this)) {
+ return (Class<? extends U>)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);
+ }
+
+ /**
+ * Copies two arrays into one. Assumes that the destination array is large
+ * enough.
+ *
+ * @param result the destination array
+ * @param head the first source array
+ * @param tail the second source array
+ * @return the destination array, that is, result
+ */
+ private static <T extends Object> T[] arraycopy(T[] result, T[] head, T[] tail) {
+ System.arraycopy(head, 0, result, 0, head.length);
+ System.arraycopy(tail, 0, result, head.length, tail.length);
+ return result;
+ }
+}
diff --git a/libdvm/src/main/java/java/lang/ClassLoader.java b/libdvm/src/main/java/java/lang/ClassLoader.java
new file mode 100644
index 0000000..dbad137
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/ClassLoader.java
@@ -0,0 +1,836 @@
+/*
+ * 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 dalvik.system.VMStack;
+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;
+
+/**
+ * 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.
+ * <p>
+ * {@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.
+ * </p>
+ * @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<String, Package> packages = new HashMap<String, Package>();
+
+ /**
+ * 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<URL> 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<URL> 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)}.
+ * <p>
+ * <strong>Note:</strong> In the Android reference implementation, the
+ * second parameter of {@link #loadClass(String, boolean)} is ignored
+ * anyway.
+ * </p>
+ *
+ * @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:
+ * <ol>
+ * <li> Call {@link #findLoadedClass(String)} to determine if the requested
+ * class has already been loaded.</li>
+ * <li>If the class has not yet been loaded: Invoke this method on the
+ * parent class loader.</li>
+ * <li>If the class has still not been loaded: Call
+ * {@link #findClass(String)} to find the class.</li>
+ * </ol>
+ * <p>
+ * <strong>Note:</strong> In the Android reference implementation, the
+ * {@code resolve} parameter is ignored; classes are never linked.
+ * </p>
+ *
+ * @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) {
+ try {
+ clazz = parent.loadClass(className, false);
+ } catch (ClassNotFoundException e) {
+ // Don't want to see this.
+ }
+
+ if (clazz == null) {
+ clazz = findClass(className);
+ }
+ }
+
+ return clazz;
+ }
+
+ /**
+ * Forces a class to be linked (initialized). If the class has already been
+ * linked this operation has no effect.
+ * <p>
+ * <strong>Note:</strong> In the Android reference implementation, this
+ * method has no effect.
+ * </p>
+ *
+ * @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<URL> 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".
+ * <p>
+ * This implementation always returns {@code null}.
+ * </p>
+ *
+ * @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<Package> 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.
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @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.
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @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.
+ * <p>
+ * <strong>Note: </strong>This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ *
+ * @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.
+ * <p>
+ * <strong>Note:</strong> This method does nothing in the Android reference
+ * implementation.
+ * </p>
+ */
+ 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<URL> {
+
+ private Enumeration<URL> first;
+
+ private Enumeration<URL> second;
+
+ public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ public boolean hasMoreElements() {
+ return first.hasMoreElements() || second.hasMoreElements();
+ }
+
+ 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 VMClassLoader.loadClass(name, false);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return VMClassLoader.getResource(name);
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected Enumeration<URL> 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.
+ * <p>
+ * 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<URL> getResources(String resName) throws IOException {
+ return findResources(resName);
+ }
+}
+
+/**
+ * TODO Open issues - Missing / empty methods - Signer stuff - Protection
+ * domains - Assertions
+ */
diff --git a/libdvm/src/main/java/java/lang/Daemons.java b/libdvm/src/main/java/java/lang/Daemons.java
new file mode 100644
index 0000000..78a4152
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/Daemons.java
@@ -0,0 +1,281 @@
+/*
+ * 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();
+ }
+
+ public static void stop() {
+ ReferenceQueueDaemon.INSTANCE.stop();
+ FinalizerDaemon.INSTANCE.stop();
+ FinalizerWatchdogDaemon.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.mSystem, 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<Object> 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);
+ }
+ }
+}
diff --git a/libdvm/src/main/java/java/lang/Object.java b/libdvm/src/main/java/java/lang/Object.java
new file mode 100644
index 0000000..4bca034
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/Object.java
@@ -0,0 +1,442 @@
+/*
+ * 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.
+ *
+ * <a name="writing_equals"><h4>Writing a correct {@code equals} method</h4></a>
+ * <p>Follow this style to write a canonical {@code equals} method:
+ * <pre>
+ * // Use @Override to avoid accidental overloading.
+ * &#x0040;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 &amp;&amp;
+ * referenceField.equals(lhs.referenceField) &amp;&amp;
+ * (nullableField == null ? lhs.nullableField == null
+ * : nullableField.equals(lhs.nullableField));
+ * }
+ * </pre>
+ * <p>If you override {@code equals}, you should also override {@code hashCode}: equal
+ * instances must have equal hash codes.
+ *
+ * <p>See <i>Effective Java</i> item 8 for much more detail and clarification.
+ *
+ * <a name="writing_hashCode"><h4>Writing a correct {@code hashCode} method</h4></a>
+ * <p>Follow this style to write a canonical {@code hashCode} method:
+ * <pre>
+ * &#x0040;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;
+ * }
+ * </pre>
+ *
+ * <p>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:
+ * <pre>
+ * &#x0040;Override public int hashCode() {
+ * throw new UnsupportedOperationException();
+ * }
+ * </pre>
+ *
+ * <p>See <i>Effective Java</i> item 9 for much more detail and clarification.
+ *
+ * <a name="writing_toString"><h4>Writing a useful {@code toString} method</h4></a>
+ * <p>For debugging convenience, it's common to override {@code toString} in this style:
+ * <pre>
+ * &#x0040;Override public String toString() {
+ * return getClass().getName() + "[" +
+ * "primitiveField=" + primitiveField + ", " +
+ * "referenceField=" + referenceField + ", " +
+ * "arrayField=" + Arrays.toString(arrayField) + "]";
+ * }
+ * </pre>
+ * <p>The set of fields to include is generally the same as those that would be tested
+ * in your {@code equals} implementation.
+ * <p>See <i>Effective Java</i> item 10 for much more detail and clarification.
+ */
+public class Object {
+ /**
+ * Constructs a new instance of {@code Object}.
+ */
+ public Object() {
+ }
+
+ /**
+ * 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 doesn't implement Cloneable");
+ }
+
+ return internalClone((Cloneable) this);
+ }
+
+ /*
+ * Native helper method for cloning.
+ */
+ private native Object internalClone(Cloneable o);
+
+ /**
+ * 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.
+ *
+ * <p>The default implementation returns {@code true} only if {@code this ==
+ * o}. See <a href="{@docRoot}reference/java/lang/Object.html#writing_equals">Writing a correct
+ * {@code equals} method</a>
+ * if you intend implementing your own {@code equals} method.
+ *
+ * <p>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.
+ *
+ * <p>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.
+ *
+ * <p>If you <i>must</i> use finalizers, consider at least providing your own
+ * {@link java.lang.ref.ReferenceQueue} and having your own thread process that queue.
+ *
+ * <p>Unlike constructors, finalizers are not automatically chained. You are responsible for
+ * calling {@code super.finalize()} yourself.
+ *
+ * <p>Uncaught exceptions thrown by finalizers are ignored and do not terminate the finalizer
+ * thread.
+ *
+ * See <i>Effective Java</i> 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<? extends Foo>} where {@code Foo} is the
+ * erasure of the type of the expression {@code getClass()} was called upon.
+ * <p>
+ * As an example, the following code actually compiles, although one might
+ * think it shouldn't:
+ * <p>
+ * <pre>{@code
+ * List<Integer> l = new ArrayList<Integer>();
+ * Class<? extends List> c = l.getClass();}</pre>
+ *
+ * @return this object's {@code Class} instance.
+ */
+ public final native Class<?> getClass();
+
+ /**
+ * 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.
+ *
+ * <p>Note that hash values must not change over time unless information used in equals
+ * comparisons also changes.
+ *
+ * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_hashCode">Writing a correct
+ * {@code hashCode} method</a>
+ * if you intend implementing your own {@code hashCode} method.
+ *
+ * @return this object's hash code.
+ * @see #equals
+ */
+ public native int hashCode();
+
+ /**
+ * 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.
+ * <p>
+ * This method can only be invoked by a thread which owns this object's
+ * monitor. A thread becomes owner of an object's monitor
+ * </p>
+ * <ul>
+ * <li>by executing a synchronized method of that object;</li>
+ * <li>by executing the body of a {@code synchronized} statement that
+ * synchronizes on the object;</li>
+ * <li>by executing a synchronized static method if the object is of type
+ * {@code Class}.</li>
+ * </ul>
+ *
+ * @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.
+ * <p>
+ * This method can only be invoked by a thread which owns this object's
+ * monitor. A thread becomes owner of an object's monitor
+ * </p>
+ * <ul>
+ * <li>by executing a synchronized method of that object;</li>
+ * <li>by executing the body of a {@code synchronized} statement that
+ * synchronizes on the object;</li>
+ * <li>by executing a synchronized static method if the object is of type
+ * {@code Class}.</li>
+ * </ul>
+ *
+ * @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:
+ * <pre>
+ * getClass().getName() + '@' + Integer.toHexString(hashCode())</pre>
+ * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_toString">Writing a useful
+ * {@code toString} method</a>
+ * 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.
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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 void wait() throws InterruptedException {
+ wait(0, 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
+ * which owns this object's monitor; see {@link #notify()} on how a thread
+ * can become the owner of a monitor.
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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.
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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/libdvm/src/main/java/java/lang/Thread.java b/libdvm/src/main/java/java/lang/Thread.java
new file mode 100644
index 0000000..cc0975f
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/Thread.java
@@ -0,0 +1,1281 @@
+/*
+ * 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.
+ *
+ * <p>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}.
+ *
+ * <p>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 */
+ volatile VMThread vmThread;
+ 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<Runnable> interruptActions = new ArrayList<Runnable>();
+
+ /**
+ * 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 parking. */
+ 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 <code>run</code> 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 <code>run</code> 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 <code>run</code> will be
+ * executed by the new {@code Thread}
+ * @throws IllegalThreadStateException
+ * if <code>group.destroy()</code> 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 <code>run</code> will be
+ * executed by the new {@code Thread}
+ * @param threadName
+ * the name for the {@code Thread} being created
+ * @throws IllegalThreadStateException
+ * if <code>group.destroy()</code> 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 <code>group.destroy()</code> 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 <code>run</code> 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 <code>group.destroy()</code> 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 not specified");
+ }
+
+ 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 <code>run</code> will
+ * be executed by the new Thread
+ * @param threadName Name for the Thread being created
+ * @param stackSize Platform dependent stack size
+ * @throws IllegalThreadStateException if <code>group.destroy()</code> 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.
+ *
+ * @return the current Thread.
+ */
+ public static Thread currentThread() {
+ return VMThread.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 <code>threads</code> 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<Thread, StackTraceElement[]> getAllStackTraces() {
+ Map<Thread, StackTraceElement[]> map = new HashMap<Thread, StackTraceElement[]>();
+
+ // 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.mSystem.activeCount();
+ Thread[] threads = new Thread[count + count / 2];
+
+ // Enumerate the threads and collect the stacktraces.
+ count = ThreadGroup.mSystem.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 <code>null</code> if
+ * none exists.
+ */
+ public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
+ return defaultUncaughtHandler;
+ }
+
+ /**
+ * Returns the thread's identifier. The ID is a positive <code>long</code>
+ * 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() {
+ // TODO This is ugly and should be implemented better.
+ VMThread vmt = this.vmThread;
+
+ // Make sure we have a valid reference to an object. If native code
+ // deletes the reference we won't run into a null reference later.
+ VMThread thread = vmThread;
+ if (thread != null) {
+ // If the Thread Object became invalid or was not yet started,
+ // getStatus() will return -1.
+ int state = thread.getStatus();
+ if(state != -1) {
+ return VMThread.STATE_MAP[state];
+ }
+ }
+ return hasBeenStarted ? Thread.State.TERMINATED : Thread.State.NEW;
+ }
+
+ /**
+ * 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 <code>null</code> 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}:
+ * <ul>
+ * <li>
+ * {@code Thread}s blocked in one of {@code Object}'s {@code wait()} methods
+ * or one of {@code Thread}'s {@code join()} or {@code sleep()} methods will
+ * be woken up, their interrupt status will be cleared, and they receive an
+ * {@link InterruptedException}.
+ * <li>
+ * {@code Thread}s blocked in an I/O operation of an
+ * {@link java.nio.channels.InterruptibleChannel} will have their interrupt
+ * status set and receive an
+ * {@link java.nio.channels.ClosedByInterruptException}. Also, the channel
+ * will be closed.
+ * <li>
+ * {@code Thread}s blocked in a {@link java.nio.channels.Selector} will have
+ * their interrupt status set and return immediately. They don't receive an
+ * exception in this case.
+ * <ul>
+ *
+ * @see Thread#interrupted
+ * @see Thread#isInterrupted
+ */
+ public void interrupt() {
+ // Interrupt this thread before running actions so that other
+ // threads that observe the interrupt as a result of an action
+ // will see that this thread is in the interrupted state.
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ vmt.interrupt();
+ }
+
+ synchronized (interruptActions) {
+ for (int i = interruptActions.size() - 1; i >= 0; i--) {
+ interruptActions.get(i).run();
+ }
+ }
+ }
+
+ /**
+ * Returns a <code>boolean</code> indicating whether the current Thread (
+ * <code>currentThread()</code>) has a pending interrupt request (<code>
+ * true</code>) or not (<code>false</code>). It also has the side-effect of
+ * clearing the flag.
+ *
+ * @return a <code>boolean</code> indicating the interrupt status
+ * @see Thread#currentThread
+ * @see Thread#interrupt
+ * @see Thread#isInterrupted
+ */
+ public static boolean interrupted() {
+ return VMThread.interrupted();
+ }
+
+ /**
+ * Returns <code>true</code> if the receiver has already been started and
+ * still runs code (hasn't died yet). Returns <code>false</code> either if
+ * the receiver hasn't been started yet or if it has already started and run
+ * to completion and died.
+ *
+ * @return a <code>boolean</code> indicating the liveness of the Thread
+ * @see Thread#start
+ */
+ public final boolean isAlive() {
+ return (vmThread != null);
+ }
+
+ /**
+ * Tests whether this is a daemon thread.
+ * A daemon thread only runs as long as there are non-daemon threads running.
+ * When the last non-daemon thread ends, the runtime will exit. This is not
+ * normally relevant to applications with a UI.
+ */
+ public final boolean isDaemon() {
+ return daemon;
+ }
+
+ /**
+ * Returns a <code>boolean</code> indicating whether the receiver has a
+ * pending interrupt request (<code>true</code>) or not (
+ * <code>false</code>)
+ *
+ * @return a <code>boolean</code> indicating the interrupt status
+ * @see Thread#interrupt
+ * @see Thread#interrupted
+ */
+ public boolean isInterrupted() {
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ return vmt.isInterrupted();
+ }
+
+ return false;
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies.
+ *
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ */
+ public final void join() throws InterruptedException {
+ VMThread t = vmThread;
+ if (t == null) {
+ return;
+ }
+
+ synchronized (t) {
+ while (isAlive()) {
+ t.wait();
+ }
+ }
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies or the specified timeout
+ * expires, whatever happens first.
+ *
+ * @param millis The maximum time to wait (in milliseconds).
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ */
+ public final void join(long millis) throws InterruptedException {
+ join(millis, 0);
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies or the specified timeout
+ * expires, whatever happens first.
+ *
+ * @param millis The maximum time to wait (in milliseconds).
+ * @param nanos Extra nanosecond precision
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ */
+ public final void join(long millis, int nanos) throws InterruptedException {
+ if (millis < 0 || nanos < 0 || nanos >= NANOS_PER_MILLI) {
+ throw new IllegalArgumentException("bad timeout: millis=" + millis + ",nanos=" + nanos);
+ }
+
+ // avoid overflow: if total > 292,277 years, just wait forever
+ boolean overflow = millis >= (Long.MAX_VALUE - nanos) / NANOS_PER_MILLI;
+ boolean forever = (millis | nanos) == 0;
+ if (forever | overflow) {
+ join();
+ return;
+ }
+
+ VMThread t = vmThread;
+ if (t == null) {
+ return;
+ }
+
+ synchronized (t) {
+ if (!isAlive()) {
+ return;
+ }
+
+ // guaranteed not to overflow
+ long nanosToWait = millis * NANOS_PER_MILLI + nanos;
+
+ // wait until this thread completes or the timeout has elapsed
+ long start = System.nanoTime();
+ while (true) {
+ t.wait(millis, nanos);
+ if (!isAlive()) {
+ break;
+ }
+ long nanosElapsed = System.nanoTime() - start;
+ long nanosRemaining = nanosToWait - nanosElapsed;
+ if (nanosRemaining <= 0) {
+ break;
+ }
+ millis = nanosRemaining / NANOS_PER_MILLI;
+ nanos = (int) (nanosRemaining - millis * NANOS_PER_MILLI);
+ }
+ }
+ }
+
+ /**
+ * Throws {@code UnsupportedOperationException}.
+ * @deprecated Only useful in conjunction with deprecated method {@link Thread#suspend}.
+ */
+ @Deprecated
+ public final void resume() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Calls the <code>run()</code> method of the Runnable object the receiver
+ * holds. If no Runnable is set, does nothing.
+ *
+ * @see Thread#start
+ */
+ public void run() {
+ if (target != null) {
+ target.run();
+ }
+ }
+
+ /**
+ * Set the context ClassLoader for the receiver.
+ *
+ * @param cl The context ClassLoader
+ * @see #getContextClassLoader()
+ */
+ public void setContextClassLoader(ClassLoader cl) {
+ contextClassLoader = cl;
+ }
+
+ /**
+ * Marks this thread as a daemon thread.
+ * A daemon thread only runs as long as there are non-daemon threads running.
+ * When the last non-daemon thread ends, the runtime will exit. This is not
+ * normally relevant to applications with a UI.
+ * @throws IllegalThreadStateException - if this thread has already started.
+ */
+ public final void setDaemon(boolean isDaemon) {
+ checkNotStarted();
+ if (vmThread == null) {
+ daemon = isDaemon;
+ }
+ }
+
+ private void checkNotStarted() {
+ if (hasBeenStarted) {
+ throw new IllegalThreadStateException("Thread already started");
+ }
+ }
+
+ /**
+ * Sets the default uncaught exception handler. This handler is invoked in
+ * case any Thread dies due to an unhandled exception.
+ *
+ * @param handler
+ * The handler to set or null.
+ */
+ public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+ Thread.defaultUncaughtHandler = handler;
+ }
+
+ /**
+ * Adds a runnable to be invoked upon interruption. If this thread has
+ * already been interrupted, the runnable will be invoked immediately. The
+ * action should be idempotent as it may be invoked multiple times for a
+ * single interruption.
+ *
+ * <p>Each call to this method must be matched with a corresponding call to
+ * {@link #popInterruptAction$}.
+ *
+ * @hide used by NIO
+ */
+ public final void pushInterruptAction$(Runnable interruptAction) {
+ synchronized (interruptActions) {
+ interruptActions.add(interruptAction);
+ }
+
+ if (interruptAction != null && isInterrupted()) {
+ interruptAction.run();
+ }
+ }
+
+ /**
+ * Removes {@code interruptAction} so it is not invoked upon interruption.
+ *
+ * @param interruptAction the pushed action, used to check that the call
+ * stack is correctly nested.
+ *
+ * @hide used by NIO
+ */
+ public final void popInterruptAction$(Runnable interruptAction) {
+ synchronized (interruptActions) {
+ Runnable removed = interruptActions.remove(interruptActions.size() - 1);
+ if (interruptAction != removed) {
+ throw new IllegalArgumentException(
+ "Expected " + interruptAction + " but was " + removed);
+ }
+ }
+ }
+
+ /**
+ * Sets the name of the Thread.
+ *
+ * @param threadName the new name for the Thread
+ * @see Thread#getName
+ */
+ public final void setName(String threadName) {
+ if (threadName == null) {
+ throw new NullPointerException("threadName == null");
+ }
+
+ name = threadName;
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ /* notify the VM that the thread name has changed */
+ vmt.nameChanged(threadName);
+ }
+ }
+
+ /**
+ * Sets the priority of this thread. If the requested priority is greater than the
+ * parent thread group's {@link java.lang.ThreadGroup#getMaxPriority}, the group's maximum
+ * priority will be used instead.
+ *
+ * @throws IllegalArgumentException - if the new priority is greater than {@link #MAX_PRIORITY}
+ * or less than {@link #MIN_PRIORITY}
+ */
+ public final void setPriority(int priority) {
+ if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
+ throw new IllegalArgumentException("Priority out of range: " + priority);
+ }
+
+ if (priority > group.getMaxPriority()) {
+ priority = group.getMaxPriority();
+ }
+
+ this.priority = priority;
+
+ VMThread vmt = this.vmThread;
+ if (vmt != null) {
+ vmt.setPriority(priority);
+ }
+ }
+
+ /**
+ * <p>
+ * Sets the uncaught exception handler. This handler is invoked in case this
+ * Thread dies due to an unhandled exception.
+ * </p>
+ *
+ * @param handler
+ * The handler to set or <code>null</code>.
+ */
+ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+ uncaughtHandler = handler;
+ }
+
+ /**
+ * Causes the thread which sent this message to sleep for the given interval
+ * of time (given in milliseconds). The precision is not guaranteed - the
+ * Thread may sleep more or less than requested.
+ *
+ * @param time
+ * The time to sleep in milliseconds.
+ * @throws InterruptedException
+ * if <code>interrupt()</code> was called for this Thread while
+ * it was sleeping
+ * @see Thread#interrupt()
+ */
+ public static void sleep(long time) throws InterruptedException {
+ Thread.sleep(time, 0);
+ }
+
+ /**
+ * Causes the thread which sent this message to sleep for the given interval
+ * of time (given in milliseconds and nanoseconds). The precision is not
+ * guaranteed - the Thread may sleep more or less than requested.
+ *
+ * @param millis
+ * The time to sleep in milliseconds.
+ * @param nanos
+ * Extra nanosecond precision
+ * @throws InterruptedException
+ * if <code>interrupt()</code> was called for this Thread while
+ * it was sleeping
+ * @see Thread#interrupt()
+ */
+ public static void sleep(long millis, int nanos) throws InterruptedException {
+ VMThread.sleep(millis, nanos);
+ }
+
+ /**
+ * Starts the new Thread of execution. The <code>run()</code> method of
+ * the receiver will be called by the receiver Thread itself (and not the
+ * Thread calling <code>start()</code>).
+ *
+ * @throws IllegalThreadStateException - if this thread has already started.
+ * @see Thread#run
+ */
+ public synchronized void start() {
+ checkNotStarted();
+
+ hasBeenStarted = true;
+
+ VMThread.create(this, stackSize);
+ }
+
+ /**
+ * Requests the receiver Thread to stop and throw ThreadDeath. The Thread is
+ * resumed if it was suspended and awakened if it was sleeping, so that it
+ * can proceed to throw ThreadDeath.
+ *
+ * @deprecated because stopping a thread in this manner is unsafe and can
+ * leave your application and the VM in an unpredictable state.
+ */
+ @Deprecated
+ public final void stop() {
+ stop(new ThreadDeath());
+ }
+
+ /**
+ * Throws {@code UnsupportedOperationException}.
+ * @deprecated because stopping a thread in this manner is unsafe and can
+ * leave your application and the VM in an unpredictable state.
+ */
+ @Deprecated
+ public final synchronized void stop(Throwable throwable) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Throws {@code UnsupportedOperationException}.
+ * @deprecated May cause deadlocks.
+ */
+ @Deprecated
+ public final void suspend() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns a string containing a concise, human-readable description of the
+ * Thread. It includes the Thread's name, priority, and group name.
+ *
+ * @return a printable representation for the receiver.
+ */
+ @Override
+ public String toString() {
+ return "Thread[" + name + "," + priority + "," + group.getName() + "]";
+ }
+
+ /**
+ * Causes the calling Thread to yield execution time to another Thread that
+ * is ready to run. The actual scheduling is implementation-dependent.
+ */
+ public static void yield() {
+ VMThread.yield();
+ }
+
+ /**
+ * Indicates whether the current Thread has a monitor lock on the specified
+ * object.
+ *
+ * @param object the object to test for the monitor lock
+ * @return true if the current thread has a monitor lock on the specified
+ * object; false otherwise
+ */
+ public static boolean holdsLock(Object object) {
+ return currentThread().vmThread.holdsLock(object);
+ }
+
+ /**
+ * Implemented by objects that want to handle cases where a thread is being
+ * terminated by an uncaught exception. Upon such termination, the handler
+ * is notified of the terminating thread and causal exception. If there is
+ * no explicit handler set then the thread's group is the default handler.
+ */
+ public static interface UncaughtExceptionHandler {
+ /**
+ * The thread is being terminated by an uncaught exception. Further
+ * exceptions thrown in this method are prevent the remainder of the
+ * method from executing, but are otherwise ignored.
+ *
+ * @param thread the thread that has an uncaught exception
+ * @param ex the exception that was thrown
+ */
+ void uncaughtException(Thread thread, Throwable ex);
+ }
+
+ /**
+ * Unparks this thread. This unblocks the thread it if it was
+ * previously parked, or indicates that the thread is "preemptively
+ * unparked" if it wasn't already parked. The latter means that the
+ * next time the thread is told to park, it will merely clear its
+ * latent park bit and carry on without blocking.
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * @hide for Unsafe
+ */
+ public void unpark() {
+ VMThread vmt = vmThread;
+
+ if (vmt == null) {
+ /*
+ * vmThread is null before the thread is start()ed. In
+ * this case, we just go ahead and set the state to
+ * PREEMPTIVELY_UNPARKED. Since this happens before the
+ * thread is started, we don't have to worry about
+ * synchronizing with it.
+ */
+ parkState = ParkState.PREEMPTIVELY_UNPARKED;
+ return;
+ }
+
+ synchronized (vmt) {
+ switch (parkState) {
+ case ParkState.PREEMPTIVELY_UNPARKED: {
+ /*
+ * Nothing to do in this case: By definition, a
+ * preemptively unparked thread is to remain in
+ * the preemptively unparked state if it is told
+ * to unpark.
+ */
+ break;
+ }
+ case ParkState.UNPARKED: {
+ parkState = ParkState.PREEMPTIVELY_UNPARKED;
+ break;
+ }
+ default /*parked*/: {
+ parkState = ParkState.UNPARKED;
+ vmt.notifyAll();
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Parks the current thread for a particular number of nanoseconds, or
+ * indefinitely. If not indefinitely, this method unparks the thread
+ * after the given number of nanoseconds if no other thread unparks it
+ * first. If the thread has been "preemptively unparked," this method
+ * cancels that unparking and returns immediately. This method may
+ * also return spuriously (that is, without the thread being told to
+ * unpark and without the indicated amount of time elapsing).
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * <p>This method must only be called when <code>this</code> is the current
+ * thread.
+ *
+ * @param nanos number of nanoseconds to park for or <code>0</code>
+ * to park indefinitely
+ * @throws IllegalArgumentException thrown if <code>nanos &lt; 0</code>
+ *
+ * @hide for Unsafe
+ */
+ public void parkFor(long nanos) {
+ VMThread vmt = vmThread;
+
+ if (vmt == null) {
+ // Running threads should always have an associated vmThread.
+ throw new AssertionError();
+ }
+
+ synchronized (vmt) {
+ switch (parkState) {
+ case ParkState.PREEMPTIVELY_UNPARKED: {
+ parkState = ParkState.UNPARKED;
+ break;
+ }
+ case ParkState.UNPARKED: {
+ long millis = nanos / NANOS_PER_MILLI;
+ nanos %= NANOS_PER_MILLI;
+
+ parkState = ParkState.PARKED;
+ try {
+ vmt.wait(millis, (int) nanos);
+ } catch (InterruptedException ex) {
+ interrupt();
+ } finally {
+ /*
+ * Note: If parkState manages to become
+ * PREEMPTIVELY_UNPARKED before hitting this
+ * code, it should left in that state.
+ */
+ if (parkState == ParkState.PARKED) {
+ parkState = ParkState.UNPARKED;
+ }
+ }
+ break;
+ }
+ default /*parked*/: {
+ throw new AssertionError(
+ "shouldn't happen: attempt to repark");
+ }
+ }
+ }
+ }
+
+ /**
+ * Parks the current thread until the specified system time. This
+ * method attempts to unpark the current thread immediately after
+ * <code>System.currentTimeMillis()</code> reaches the specified
+ * value, if no other thread unparks it first. If the thread has
+ * been "preemptively unparked," this method cancels that
+ * unparking and returns immediately. This method may also return
+ * spuriously (that is, without the thread being told to unpark
+ * and without the indicated amount of time elapsing).
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * <p>This method must only be called when <code>this</code> is the
+ * current thread.
+ *
+ * @param time the time after which the thread should be unparked,
+ * in absolute milliseconds-since-the-epoch
+ *
+ * @hide for Unsafe
+ */
+ public void parkUntil(long time) {
+ VMThread vmt = vmThread;
+
+ if (vmt == null) {
+ // Running threads should always have an associated vmThread.
+ throw new AssertionError();
+ }
+
+ synchronized (vmt) {
+ /*
+ * Note: This conflates the two time bases of "wall clock"
+ * time and "monotonic uptime" time. However, given that
+ * the underlying system can only wait on monotonic time,
+ * it is unclear if there is any way to avoid the
+ * conflation. The downside here is that if, having
+ * calculated the delay, the wall clock gets moved ahead,
+ * this method may not return until well after the wall
+ * clock has reached the originally designated time. The
+ * reverse problem (the wall clock being turned back)
+ * isn't a big deal, since this method is allowed to
+ * spuriously return for any reason, and this situation
+ * can safely be construed as just such a spurious return.
+ */
+ long delayMillis = time - System.currentTimeMillis();
+
+ if (delayMillis <= 0) {
+ parkState = ParkState.UNPARKED;
+ } else {
+ parkFor(delayMillis * NANOS_PER_MILLI);
+ }
+ }
+ }
+}
diff --git a/libdvm/src/main/java/java/lang/ThreadGroup.java b/libdvm/src/main/java/java/lang/ThreadGroup.java
new file mode 100644
index 0000000..e99e99f
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/ThreadGroup.java
@@ -0,0 +1,726 @@
+/*
+ * 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.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import libcore.util.CollectionUtils;
+
+/**
+ * {@code ThreadGroup} is a means of organizing threads into a hierarchical structure.
+ * This class is obsolete. See <i>Effective Java</i> Item 73, "Avoid thread groups" for details.
+ * @see Thread
+ */
+public class ThreadGroup implements Thread.UncaughtExceptionHandler {
+
+ // Name of this ThreadGroup
+ // VM needs this field name for debugging.
+ private String name;
+
+ // Maximum priority for Threads inside this ThreadGroup
+ private int maxPriority = Thread.MAX_PRIORITY;
+
+ // The ThreadGroup to which this ThreadGroup belongs
+ // VM needs this field name for debugging.
+ final ThreadGroup parent;
+
+ /**
+ * Weak references to the threads in this group.
+ * Access is guarded by synchronizing on this field.
+ */
+ private final List<WeakReference<Thread>> threadRefs = new ArrayList<WeakReference<Thread>>(5);
+
+ /**
+ * View of the threads.
+ * Access is guarded by synchronizing on threadRefs.
+ */
+ private final Iterable<Thread> threads = CollectionUtils.dereferenceIterable(threadRefs, true);
+
+ /**
+ * Thread groups. Access is guarded by synchronizing on this field.
+ */
+ private final List<ThreadGroup> groups = new ArrayList<ThreadGroup>(3);
+
+ // Whether this ThreadGroup is a daemon ThreadGroup or not
+ private boolean isDaemon;
+
+ // Whether this ThreadGroup has already been destroyed or not
+ private boolean isDestroyed;
+
+ /* the VM uses these directly; do not rename */
+ static final ThreadGroup mSystem = new ThreadGroup();
+ static final ThreadGroup mMain = new ThreadGroup(mSystem, "main");
+
+ /**
+ * Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup}
+ * will be child of the {@code ThreadGroup} to which the calling thread belongs.
+ *
+ * @param name the name
+ * @see Thread#currentThread
+ */
+ public ThreadGroup(String name) {
+ this(Thread.currentThread().getThreadGroup(), name);
+ }
+
+ /**
+ * Constructs a new {@code ThreadGroup} with the given name, as a child of the
+ * given {@code ThreadGroup}.
+ *
+ * @param parent the parent
+ * @param name the name
+ * @throws NullPointerException if {@code parent == null}
+ * @throws IllegalThreadStateException if {@code parent} has been
+ * destroyed already
+ */
+ public ThreadGroup(ThreadGroup parent, String name) {
+ if (parent == null) {
+ throw new NullPointerException("parent == null");
+ }
+ this.name = name;
+ this.parent = parent;
+ if (parent != null) {
+ parent.add(this);
+ this.setMaxPriority(parent.getMaxPriority());
+ if (parent.isDaemon()) {
+ this.setDaemon(true);
+ }
+ }
+ }
+
+ /**
+ * Initialize the special "system" ThreadGroup. Was "main" in Harmony,
+ * but we have an additional group above that in Android.
+ */
+ private ThreadGroup() {
+ this.name = "system";
+ this.parent = null;
+ }
+
+ /**
+ * Returns the number of running {@code Thread}s which are children of this thread group,
+ * directly or indirectly.
+ *
+ * @return the number of children
+ */
+ public int activeCount() {
+ int count = 0;
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ if (thread.isAlive()) {
+ count++;
+ }
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ count += group.activeCount();
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns the number of {@code ThreadGroup}s which are children of this group,
+ * directly or indirectly.
+ *
+ * @return the number of children
+ */
+ public int activeGroupCount() {
+ int count = 0;
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ // One for this group & the subgroups
+ count += 1 + group.activeGroupCount();
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Adds a {@code ThreadGroup} to this thread group.
+ *
+ * @param g ThreadGroup to add
+ * @throws IllegalThreadStateException if this group has been destroyed already
+ */
+ private void add(ThreadGroup g) throws IllegalThreadStateException {
+ synchronized (groups) {
+ if (isDestroyed) {
+ throw new IllegalThreadStateException();
+ }
+ groups.add(g);
+ }
+ }
+
+ /**
+ * Does nothing. The definition of this method depends on the deprecated
+ * method {@link #suspend()}. The exact behavior of this call was never
+ * specified.
+ *
+ * @param b Used to control low memory implicit suspension
+ * @return {@code true} (always)
+ *
+ * @deprecated Required deprecated method suspend().
+ */
+ @Deprecated
+ public boolean allowThreadSuspension(boolean b) {
+ // Does not apply to this VM, no-op
+ return true;
+ }
+
+ /**
+ * Does nothing.
+ */
+ public final void checkAccess() {
+ }
+
+ /**
+ * Destroys this thread group and recursively all its subgroups. It is only legal
+ * to destroy a {@code ThreadGroup} that has no threads in it. Any daemon
+ * {@code ThreadGroup} is destroyed automatically when it becomes empty (no threads
+ * or thread groups in it).
+ *
+ * @throws IllegalThreadStateException if this thread group or any of its
+ * subgroups has been destroyed already or if it still contains
+ * threads.
+ */
+ public final void destroy() {
+ synchronized (threadRefs) {
+ synchronized (groups) {
+ if (isDestroyed) {
+ throw new IllegalThreadStateException(
+ "Thread group was already destroyed: "
+ + (this.name != null ? this.name : "n/a"));
+ }
+ if (threads.iterator().hasNext()) {
+ throw new IllegalThreadStateException(
+ "Thread group still contains threads: "
+ + (this.name != null ? this.name : "n/a"));
+ }
+ // Call recursively for subgroups
+ while (!groups.isEmpty()) {
+ // We always get the first element - remember, when the
+ // child dies it removes itself from our collection. See
+ // below.
+ groups.get(0).destroy();
+ }
+
+ if (parent != null) {
+ parent.remove(this);
+ }
+
+ // Now that the ThreadGroup is really destroyed it can be tagged as so
+ this.isDestroyed = true;
+ }
+ }
+ }
+
+ /*
+ * Auxiliary method that destroys this thread group and recursively all its
+ * subgroups if this is a daemon ThreadGroup.
+ *
+ * @see #destroy
+ * @see #setDaemon
+ * @see #isDaemon
+ */
+ private void destroyIfEmptyDaemon() {
+ // Has to be non-destroyed daemon to make sense
+ synchronized (threadRefs) {
+ if (isDaemon && !isDestroyed && !threads.iterator().hasNext()) {
+ synchronized (groups) {
+ if (groups.isEmpty()) {
+ destroy();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates over all active threads in this group (and its sub-groups) and
+ * stores the threads in the given array. Returns when the array is full or
+ * no more threads remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any threads that don't fit in the
+ * supplied array.
+ *
+ * @param threads the array into which the {@code Thread}s will be copied
+ * @return the number of {@code Thread}s that were copied
+ */
+ public int enumerate(Thread[] threads) {
+ return enumerate(threads, true);
+ }
+
+ /**
+ * Iterates over all active threads in this group (and, optionally, its
+ * sub-groups) and stores the threads in the given array. Returns when the
+ * array is full or no more threads remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any threads that don't fit in the
+ * supplied array.
+ *
+ * @param threads the array into which the {@code Thread}s will be copied
+ * @param recurse indicates whether {@code Thread}s in subgroups should be
+ * recursively copied as well
+ * @return the number of {@code Thread}s that were copied
+ */
+ public int enumerate(Thread[] threads, boolean recurse) {
+ return enumerateGeneric(threads, recurse, 0, true);
+ }
+
+ /**
+ * Iterates over all thread groups in this group (and its sub-groups) and
+ * and stores the groups in the given array. Returns when the array is full
+ * or no more groups remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any thread groups that don't fit in the
+ * supplied array.
+ *
+ * @param groups the array into which the {@code ThreadGroup}s will be copied
+ * @return the number of {@code ThreadGroup}s that were copied
+ */
+ public int enumerate(ThreadGroup[] groups) {
+ return enumerate(groups, true);
+ }
+
+ /**
+ * Iterates over all thread groups in this group (and, optionally, its
+ * sub-groups) and stores the groups in the given array. Returns when
+ * the array is full or no more groups remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any thread groups that don't fit in the
+ * supplied array.
+ *
+ * @param groups the array into which the {@code ThreadGroup}s will be copied
+ * @param recurse indicates whether {@code ThreadGroup}s in subgroups should be
+ * recursively copied as well or not
+ * @return the number of {@code ThreadGroup}s that were copied
+ */
+ public int enumerate(ThreadGroup[] groups, boolean recurse) {
+ return enumerateGeneric(groups, recurse, 0, false);
+ }
+
+ /**
+ * Copies into <param>enumeration</param> starting at
+ * <param>enumerationIndex</param> all Threads or ThreadGroups in the
+ * receiver. If <param>recurse</param> is true, recursively enumerate the
+ * elements in subgroups.
+ *
+ * If the array passed as parameter is too small no exception is thrown -
+ * the extra elements are simply not copied.
+ *
+ * @param enumeration array into which the elements will be copied
+ * @param recurse Indicates whether subgroups should be enumerated or not
+ * @param enumerationIndex Indicates in which position of the enumeration
+ * array we are
+ * @param enumeratingThreads Indicates whether we are enumerating Threads or
+ * ThreadGroups
+ * @return How many elements were enumerated/copied over
+ */
+ private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex,
+ boolean enumeratingThreads) {
+ if (enumeratingThreads) {
+ synchronized (threadRefs) {
+ // walk the references directly so we can iterate in reverse order
+ for (int i = threadRefs.size() - 1; i >= 0; --i) {
+ Thread thread = threadRefs.get(i).get();
+ if (thread != null && thread.isAlive()) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumeration[enumerationIndex++] = thread;
+ }
+ }
+ }
+ } else {
+ synchronized (groups) {
+ for (int i = groups.size() - 1; i >= 0; --i) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumeration[enumerationIndex++] = groups.get(i);
+ }
+ }
+ }
+
+ if (recurse) {
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumerationIndex = group.enumerateGeneric(enumeration, recurse,
+ enumerationIndex, enumeratingThreads);
+ }
+ }
+ }
+ return enumerationIndex;
+ }
+
+ /**
+ * Returns the maximum allowed priority for a {@code Thread} in this thread group.
+ *
+ * @return the maximum priority
+ *
+ * @see #setMaxPriority
+ */
+ public final int getMaxPriority() {
+ return maxPriority;
+ }
+
+ /**
+ * Returns the name of this thread group.
+ *
+ * @return the group's name
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * Returns this thread group's parent {@code ThreadGroup}. It can be null if this
+ * is the the root ThreadGroup.
+ *
+ * @return the parent
+ */
+ public final ThreadGroup getParent() {
+ return parent;
+ }
+
+ /**
+ * Interrupts every {@code Thread} in this group and recursively in all its
+ * subgroups.
+ *
+ * @see Thread#interrupt
+ */
+ public final void interrupt() {
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ thread.interrupt();
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.interrupt();
+ }
+ }
+ }
+
+ /**
+ * Checks whether this thread group is a daemon {@code ThreadGroup}.
+ *
+ * @return true if this thread group is a daemon {@code ThreadGroup}
+ *
+ * @see #setDaemon
+ * @see #destroy
+ */
+ public final boolean isDaemon() {
+ return isDaemon;
+ }
+
+ /**
+ * Checks whether this thread group has already been destroyed.
+ *
+ * @return true if this thread group has already been destroyed
+ * @see #destroy
+ */
+ public synchronized boolean isDestroyed() {
+ return isDestroyed;
+ }
+
+ /**
+ * Outputs to {@code System.out} a text representation of the
+ * hierarchy of {@code Thread}s and {@code ThreadGroup}s in this thread group (and recursively).
+ * Proper indentation is used to show the nesting of groups inside groups
+ * and threads inside groups.
+ */
+ public void list() {
+ // We start in a fresh line
+ System.out.println();
+ list(0);
+ }
+
+ /*
+ * Outputs to {@code System.out}a text representation of the
+ * hierarchy of Threads and ThreadGroups in this thread group (and recursively).
+ * The indentation will be four spaces per level of nesting.
+ *
+ * @param levels How many levels of nesting, so that proper indentation can
+ * be output.
+ */
+ private void list(int levels) {
+ indent(levels);
+ System.out.println(this.toString());
+
+ ++levels;
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ indent(levels);
+ System.out.println(thread);
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.list(levels);
+ }
+ }
+ }
+
+ private void indent(int levels) {
+ for (int i = 0; i < levels; i++) {
+ System.out.print(" "); // 4 spaces for each level
+ }
+ }
+
+ /**
+ * Checks whether this thread group is a direct or indirect parent group of a
+ * given {@code ThreadGroup}.
+ *
+ * @param g the potential child {@code ThreadGroup}
+ * @return true if this thread group is parent of {@code g}
+ */
+ public final boolean parentOf(ThreadGroup g) {
+ while (g != null) {
+ if (this == g) {
+ return true;
+ }
+ g = g.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Removes an immediate subgroup.
+ *
+ * @param g ThreadGroup to remove
+ *
+ * @see #add(Thread)
+ * @see #add(ThreadGroup)
+ */
+ private void remove(ThreadGroup g) {
+ synchronized (groups) {
+ for (Iterator<ThreadGroup> i = groups.iterator(); i.hasNext(); ) {
+ ThreadGroup threadGroup = i.next();
+ if (threadGroup.equals(g)) {
+ i.remove();
+ break;
+ }
+ }
+ }
+ destroyIfEmptyDaemon();
+ }
+
+ /**
+ * Resumes every thread in this group and recursively in all its
+ * subgroups.
+ *
+ * @see Thread#resume
+ * @see #suspend
+ *
+ * @deprecated Requires deprecated method Thread.resume().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void resume() {
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ thread.resume();
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.resume();
+ }
+ }
+ }
+
+ /**
+ * Sets whether this is a daemon {@code ThreadGroup} or not. Daemon
+ * thread groups are automatically destroyed when they become empty.
+ *
+ * @param isDaemon the new value
+ * @see #isDaemon
+ * @see #destroy
+ */
+ public final void setDaemon(boolean isDaemon) {
+ this.isDaemon = isDaemon;
+ }
+
+ /**
+ * Configures the maximum allowed priority for a {@code Thread} in this group and
+ * recursively in all its subgroups.
+ *
+ * <p>A caller can never increase the maximum priority of a thread group.
+ * Such an attempt will not result in an exception, it will
+ * simply leave the thread group with its current maximum priority.
+ *
+ * @param newMax the new maximum priority to be set
+ *
+ * @throws IllegalArgumentException if the new priority is greater than
+ * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY
+ *
+ * @see #getMaxPriority
+ */
+ public final void setMaxPriority(int newMax) {
+ if (newMax <= this.maxPriority) {
+ if (newMax < Thread.MIN_PRIORITY) {
+ newMax = Thread.MIN_PRIORITY;
+ }
+
+ int parentPriority = parent == null ? newMax : parent.getMaxPriority();
+ this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.setMaxPriority(newMax);
+ }
+ }
+ }
+ }
+
+ /**
+ * Stops every thread in this group and recursively in all its subgroups.
+ *
+ * @see Thread#stop()
+ * @see Thread#stop(Throwable)
+ * @see ThreadDeath
+ *
+ * @deprecated Requires deprecated method Thread.stop().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void stop() {
+ if (stopHelper()) {
+ Thread.currentThread().stop();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean stopHelper() {
+ boolean stopCurrent = false;
+ synchronized (threadRefs) {
+ Thread current = Thread.currentThread();
+ for (Thread thread : threads) {
+ if (thread == current) {
+ stopCurrent = true;
+ } else {
+ thread.stop();
+ }
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ stopCurrent |= group.stopHelper();
+ }
+ }
+ return stopCurrent;
+ }
+
+ /**
+ * Suspends every thread in this group and recursively in all its
+ * subgroups.
+ *
+ * @see Thread#suspend
+ * @see #resume
+ *
+ * @deprecated Requires deprecated method Thread.suspend().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void suspend() {
+ if (suspendHelper()) {
+ Thread.currentThread().suspend();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean suspendHelper() {
+ boolean suspendCurrent = false;
+ synchronized (threadRefs) {
+ Thread current = Thread.currentThread();
+ for (Thread thread : threads) {
+ if (thread == current) {
+ suspendCurrent = true;
+ } else {
+ thread.suspend();
+ }
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ suspendCurrent |= group.suspendHelper();
+ }
+ }
+ return suspendCurrent;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[name=" + getName()
+ + ",maxPriority=" + getMaxPriority() + "]";
+ }
+
+ /**
+ * Handles uncaught exceptions. Any uncaught exception in any {@code Thread}
+ * is forwarded to the thread's {@code ThreadGroup} by invoking this
+ * method.
+ *
+ * <p>New code should use {@link Thread#setUncaughtExceptionHandler} instead of thread groups.
+ *
+ * @param t the Thread that terminated with an uncaught exception
+ * @param e the uncaught exception itself
+ */
+ public void uncaughtException(Thread t, Throwable e) {
+ if (parent != null) {
+ parent.uncaughtException(t, e);
+ } else if (Thread.getDefaultUncaughtExceptionHandler() != null) {
+ // TODO The spec is unclear regarding this. What do we do?
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e);
+ } else if (!(e instanceof ThreadDeath)) {
+ // No parent group, has to be 'system' Thread Group
+ e.printStackTrace(System.err);
+ }
+ }
+
+ /**
+ * Called by the Thread constructor.
+ */
+ final void addThread(Thread thread) throws IllegalThreadStateException {
+ synchronized (threadRefs) {
+ if (isDestroyed) {
+ throw new IllegalThreadStateException();
+ }
+ threadRefs.add(new WeakReference<Thread>(thread));
+ }
+ }
+
+ /**
+ * Called by the VM when a Thread dies.
+ */
+ final void removeThread(Thread thread) throws IllegalThreadStateException {
+ synchronized (threadRefs) {
+ for (Iterator<Thread> i = threads.iterator(); i.hasNext(); ) {
+ if (i.next().equals(thread)) {
+ i.remove();
+ break;
+ }
+ }
+ }
+ destroyIfEmptyDaemon();
+ }
+}
diff --git a/libdvm/src/main/java/java/lang/VMClassLoader.java b/libdvm/src/main/java/java/lang/VMClassLoader.java
new file mode 100644
index 0000000..d7162c6
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/VMClassLoader.java
@@ -0,0 +1,85 @@
+/*
+ * 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 java.lang;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+class VMClassLoader {
+
+ /**
+ * Get a resource from a file in the bootstrap class path.
+ *
+ * It would be simpler to just walk through the class path elements
+ * ourselves, but that would require reopening Jar files.
+ *
+ * We assume that the bootclasspath can't change once the VM has
+ * started. This assumption seems to be supported by the spec.
+ */
+ static URL getResource(String name) {
+ int numEntries = getBootClassPathSize();
+ for (int i = 0; i < numEntries; i++) {
+ String urlStr = getBootClassPathResource(name, i);
+ if (urlStr != null) {
+ try {
+ return new URL(urlStr);
+ } catch (MalformedURLException mue) {
+ mue.printStackTrace();
+ // unexpected; keep going
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Get an enumeration with all matching resources.
+ */
+ static List<URL> getResources(String name) {
+ ArrayList<URL> list = new ArrayList<URL>();
+ int numEntries = getBootClassPathSize();
+ for (int i = 0; i < numEntries; i++) {
+ String urlStr = getBootClassPathResource(name, i);
+ if (urlStr != null) {
+ try {
+ list.add(new URL(urlStr));
+ } catch (MalformedURLException mue) {
+ mue.printStackTrace();
+ // unexpected; keep going
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Load class with bootstrap class loader.
+ */
+ native static Class loadClass(String name, boolean resolve) throws ClassNotFoundException;
+
+ native static Class getPrimitiveClass(char type);
+
+ native static Class findLoadedClass(ClassLoader cl, String name);
+
+ /**
+ * Boot class path manipulation, for getResources().
+ */
+ native private static int getBootClassPathSize();
+ native private static String getBootClassPathResource(String name, int index);
+}
diff --git a/libdvm/src/main/java/java/lang/VMThread.java b/libdvm/src/main/java/java/lang/VMThread.java
new file mode 100644
index 0000000..01f6ee3
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/VMThread.java
@@ -0,0 +1,77 @@
+/*
+ * 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 java.lang;
+
+class VMThread {
+ Thread thread;
+ int vmData;
+
+ VMThread(Thread t) {
+ thread = t;
+ }
+
+ native static void create(Thread t, long stackSize);
+
+ static native Thread currentThread();
+ static native boolean interrupted();
+ static native void sleep (long msec, int nsec) throws InterruptedException;
+ static native void yield();
+
+ native void interrupt();
+
+ native boolean isInterrupted();
+
+ /**
+ * Starts the VMThread (and thus the Java Thread) with the given
+ * stack size.
+ */
+ void start(long stackSize) {
+ VMThread.create(thread, stackSize);
+ }
+
+ /**
+ * Queries whether this Thread holds a monitor lock on the
+ * given object.
+ */
+ native boolean holdsLock(Object object);
+
+ native void setPriority(int newPriority);
+ native int getStatus();
+
+ /**
+ * Holds a mapping from native Thread statuses to Java one. Required for
+ * translating back the result of getStatus().
+ */
+ static final Thread.State[] STATE_MAP = new Thread.State[] {
+ Thread.State.TERMINATED, // ZOMBIE
+ Thread.State.RUNNABLE, // RUNNING
+ Thread.State.TIMED_WAITING, // TIMED_WAIT
+ Thread.State.BLOCKED, // MONITOR
+ Thread.State.WAITING, // WAIT
+ Thread.State.NEW, // INITIALIZING
+ Thread.State.NEW, // STARTING
+ Thread.State.RUNNABLE, // NATIVE
+ Thread.State.WAITING, // VMWAIT
+ Thread.State.RUNNABLE // SUSPENDED
+ };
+
+ /**
+ * Tell the VM that the thread's name has changed. This is useful for
+ * DDMS, which would otherwise be oblivious to Thread.setName calls.
+ */
+ native void nameChanged(String newName);
+}
diff --git a/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java b/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java
new file mode 100644
index 0000000..9c6b8c7
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -0,0 +1,320 @@
+/*
+ * 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.reflect;
+
+import java.lang.annotation.Annotation;
+import java.util.Hashtable;
+import org.apache.harmony.kernel.vm.StringUtils;
+
+/**
+ * {@code AccessibleObject} is the superclass of all member reflection classes
+ * (Field, Constructor, Method). AccessibleObject provides the ability to toggle
+ * a flag controlling access checks for these objects. By default, accessing a
+ * member (for example, setting a field or invoking a method) checks the
+ * validity of the access (for example, invoking a private method from outside
+ * the defining class is prohibited) and throws IllegalAccessException if the
+ * operation is not permitted. If the accessible flag is set to true, these
+ * checks are omitted. This allows privileged code, such as Java object
+ * serialization, object inspectors, and debuggers to have complete access to
+ * objects.
+ *
+ * @see Field
+ * @see Constructor
+ * @see Method
+ */
+public class AccessibleObject implements AnnotatedElement {
+
+ // If true, object is accessible, bypassing normal access checks
+ boolean flag = false;
+
+ // Holds a mapping from Java type names to native type codes.
+ static Hashtable<String, String> trans;
+
+ static {
+ trans = new Hashtable<String, String>(9);
+ trans.put("byte", "B");
+ trans.put("char", "C");
+ trans.put("short", "S");
+ trans.put("int", "I");
+ trans.put("long", "J");
+ trans.put("float", "F");
+ trans.put("double", "D");
+ trans.put("void", "V");
+ trans.put("boolean", "Z");
+ }
+
+ /**
+ * Attempts to set the value of the accessible flag for all the objects in
+ * the array provided. Setting this
+ * flag to {@code false} will enable access checks, setting to {@code true}
+ * will disable them.
+ *
+ * @param objects
+ * the accessible objects
+ * @param flag
+ * the new value for the accessible flag
+ *
+ * @see #setAccessible(boolean)
+ */
+ public static void setAccessible(AccessibleObject[] objects, boolean flag) {
+ synchronized(AccessibleObject.class) {
+ for (AccessibleObject object : objects) {
+ object.flag = flag;
+ }
+ }
+ }
+
+ /**
+ * Constructs a new {@code AccessibleObject} instance. {@code
+ * AccessibleObject} instances can only be constructed by the virtual
+ * machine.
+ */
+ protected AccessibleObject() {
+ }
+
+ /**
+ * Indicates whether this object is accessible without access checks being
+ * performed. Returns the accessible flag.
+ *
+ * @return {@code true} if this object is accessible without access
+ * checks, {@code false} otherwise
+ */
+ public boolean isAccessible() {
+ return flag;
+ }
+
+ /**
+ * Attempts to set the value of the accessible flag. Setting this flag to
+ * {@code false} will enable access checks, setting to {@code true} will
+ * disable them.
+ *
+ * @param flag
+ * the new value for the accessible flag
+ */
+ public void setAccessible(boolean flag) {
+ this.flag = flag;
+ }
+
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Annotation[] getDeclaredAnnotations() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Annotation[] getAnnotations() {
+ // for all but Class, getAnnotations == getDeclaredAnnotations
+ return getDeclaredAnnotations();
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the signature for a class. This is the kind of signature used
+ * internally by the JVM, with one-character codes representing the basic
+ * types. It is not suitable for printing.
+ *
+ * @param clazz
+ * the class for which a signature is required
+ *
+ * @return The signature as a string
+ */
+ String getSignature(Class<?> clazz) {
+ String result = "";
+ String nextType = clazz.getName();
+
+ if(trans.containsKey(nextType)) {
+ result = trans.get(nextType);
+ } else {
+ if(clazz.isArray()) {
+ result = "[" + getSignature(clazz.getComponentType());
+ } else {
+ result = "L" + nextType + ";";
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a printable String consisting of the canonical names of the
+ * classes contained in an array. The form is that used in parameter and
+ * exception lists, that is, the class or type names are separated by
+ * commas.
+ *
+ * @param types
+ * the array of classes
+ *
+ * @return The String of names
+ */
+ String toString(Class<?>[] types) {
+ StringBuilder result = new StringBuilder();
+
+ if (types.length != 0) {
+ appendTypeName(result, types[0]);
+ for (int i = 1; i < types.length; i++) {
+ result.append(',');
+ appendTypeName(result, types[i]);
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Gets the Signature attribute for this instance. Returns {@code null}
+ * if not found.
+ */
+ /*package*/ String getSignatureAttribute() {
+ /*
+ * Note: This method would have been declared abstract, but the
+ * standard API lists this class as concrete.
+ */
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Retrieve the signature attribute from an arbitrary class. This is
+ * the same as Class.getSignatureAttribute(), but it can be used from
+ * the java.lang.reflect package.
+ */
+ /*package*/ static String getClassSignatureAttribute(Class clazz) {
+ Object[] annotation = getClassSignatureAnnotation(clazz);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Retrieve the signature annotation from an arbitrary class. This is
+ * the same as Class.getSignatureAttribute(), but it can be used from
+ * the java.lang.reflect package.
+ */
+ private static native Object[] getClassSignatureAnnotation(Class clazz);
+
+ /**
+ * Appends the best {@link #toString} name for {@code c} to {@code out}.
+ * This works around the fact that {@link Class#getName} is lousy for
+ * primitive arrays (it writes "[C" instead of "char[]") and {@link
+ * Class#getCanonicalName()} is lousy for nested classes (it uses a "."
+ * separator rather than a "$" separator).
+ */
+ void appendTypeName(StringBuilder out, Class<?> c) {
+ int dimensions = 0;
+ while (c.isArray()) {
+ c = c.getComponentType();
+ dimensions++;
+ }
+ out.append(c.getName());
+ for (int d = 0; d < dimensions; d++) {
+ out.append("[]");
+ }
+ }
+
+ /**
+ * Appends names of the specified array classes to the buffer. The array
+ * elements may represent a simple type, a reference type or an array type.
+ * Output format: java.lang.Object[], java.io.File, void
+ *
+ * @param types array of classes to print the names
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void appendArrayGenericType(StringBuilder sb, Type[] types) {
+ if (types.length > 0) {
+ appendGenericType(sb, types[0]);
+ for (int i = 1; i < types.length; i++) {
+ sb.append(',');
+ appendGenericType(sb, types[i]);
+ }
+ }
+ }
+
+ /**
+ * Appends the generic type representation to the buffer.
+ *
+ * @param sb buffer
+ * @param obj the generic type which representation should be appended to the buffer
+ *
+ * @throws NullPointerException if any of the arguments is null
+ */
+ void appendGenericType(StringBuilder sb, Type obj) {
+ if (obj instanceof TypeVariable) {
+ sb.append(((TypeVariable)obj).getName());
+ } else if (obj instanceof ParameterizedType) {
+ sb.append(obj.toString());
+ } else if (obj instanceof GenericArrayType) { //XXX: is it a working branch?
+ Type simplified = ((GenericArrayType)obj).getGenericComponentType();
+ appendGenericType(sb, simplified);
+ sb.append("[]");
+ } else if (obj instanceof Class) {
+ Class c = ((Class<?>)obj);
+ if (c.isArray()){
+ String as[] = c.getName().split("\\[");
+ int len = as.length-1;
+ if (as[len].length() > 1){
+ sb.append(as[len].substring(1, as[len].length()-1));
+ } else {
+ char ch = as[len].charAt(0);
+ if (ch == 'I')
+ sb.append("int");
+ else if (ch == 'B')
+ sb.append("byte");
+ else if (ch == 'J')
+ sb.append("long");
+ else if (ch == 'F')
+ sb.append("float");
+ else if (ch == 'D')
+ sb.append("double");
+ else if (ch == 'S')
+ sb.append("short");
+ else if (ch == 'C')
+ sb.append("char");
+ else if (ch == 'Z')
+ sb.append("boolean");
+ else if (ch == 'V') //XXX: is it a working branch?
+ sb.append("void");
+ }
+ for (int i = 0; i < len; i++){
+ sb.append("[]");
+ }
+ } else {
+ sb.append(c.getName());
+ }
+ }
+ }
+}
diff --git a/libdvm/src/main/java/java/lang/reflect/Constructor.java b/libdvm/src/main/java/java/lang/reflect/Constructor.java
new file mode 100644
index 0000000..9a0e03c
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/reflect/Constructor.java
@@ -0,0 +1,460 @@
+/*
+ * 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.reflect;
+
+import java.lang.annotation.Annotation;
+import libcore.util.EmptyArray;
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.ListOfTypes;
+import org.apache.harmony.luni.lang.reflect.Types;
+
+/**
+ * This class represents a constructor. Information about the constructor can be
+ * accessed, and the constructor can be invoked dynamically.
+ *
+ * @param <T> the class that declares this constructor
+ */
+public final class Constructor<T> extends AccessibleObject implements GenericDeclaration,
+ Member {
+
+ Class<T> declaringClass;
+
+ Class<?>[] parameterTypes;
+
+ Class<?>[] exceptionTypes;
+
+ ListOfTypes genericExceptionTypes;
+ ListOfTypes genericParameterTypes;
+ TypeVariable<Constructor<T>>[] formalTypeParameters;
+ private volatile boolean genericTypesAreInitialized = false;
+
+ private synchronized void initGenericTypes() {
+ if (!genericTypesAreInitialized) {
+ String signatureAttribute = getSignatureAttribute();
+ GenericSignatureParser parser = new GenericSignatureParser(
+ declaringClass.getClassLoader());
+ parser.parseForConstructor(this, signatureAttribute, exceptionTypes);
+ formalTypeParameters = parser.formalTypeParameters;
+ genericParameterTypes = parser.parameterTypes;
+ genericExceptionTypes = parser.exceptionTypes;
+ genericTypesAreInitialized = true;
+ }
+ }
+
+ int slot;
+
+ /**
+ * Prevent this class from being instantiated.
+ */
+ private Constructor(){
+ //do nothing
+ }
+
+ /**
+ * Creates an instance of the class. Only called from native code, thus
+ * private.
+ *
+ * @param declaringClass
+ * the class this constructor object belongs to
+ * @param ptypes
+ * the parameter types of the constructor
+ * @param extypes
+ * the exception types of the constructor
+ * @param slot
+ * the slot of the constructor inside the VM class structure
+ */
+ private Constructor (Class<T> declaringClass, Class<?>[] ptypes, Class<?>[] extypes, int slot){
+ this.declaringClass = declaringClass;
+ this.parameterTypes = ptypes;
+ this.exceptionTypes = extypes; // may be null
+ this.slot = slot;
+ }
+
+ @Override /*package*/ String getSignatureAttribute() {
+ Object[] annotation = Method.getSignatureAnnotation(declaringClass, slot);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ public TypeVariable<Constructor<T>>[] getTypeParameters() {
+ initGenericTypes();
+ return formalTypeParameters.clone();
+ }
+
+ /**
+ * Returns the string representation of the constructor's declaration,
+ * including the type parameters.
+ *
+ * @return the string representation of the constructor's declaration
+ */
+ public String toGenericString() {
+ StringBuilder sb = new StringBuilder(80);
+ initGenericTypes();
+ // append modifiers if any
+ int modifier = getModifiers();
+ if (modifier != 0) {
+ sb.append(Modifier.toString(modifier & ~Modifier.VARARGS)).append(' ');
+ }
+ // append type parameters
+ if (formalTypeParameters != null && formalTypeParameters.length > 0) {
+ sb.append('<');
+ for (int i = 0; i < formalTypeParameters.length; i++) {
+ appendGenericType(sb, formalTypeParameters[i]);
+ if (i < formalTypeParameters.length - 1) {
+ sb.append(",");
+ }
+ }
+ sb.append("> ");
+ }
+ // append constructor name
+ appendTypeName(sb, getDeclaringClass());
+ // append parameters
+ sb.append('(');
+ appendArrayGenericType(sb,
+ Types.getClonedTypeArray(genericParameterTypes));
+ sb.append(')');
+ // append exceptions if any
+ Type[] genericExceptionTypeArray =
+ Types.getClonedTypeArray(genericExceptionTypes);
+ if (genericExceptionTypeArray.length > 0) {
+ sb.append(" throws ");
+ appendArrayGenericType(sb, genericExceptionTypeArray);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the generic parameter types as an array of {@code Type}
+ * instances, in declaration order. If this constructor has no generic
+ * parameters, an empty array is returned.
+ *
+ * @return the parameter types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic constructor signature is invalid
+ * @throws TypeNotPresentException
+ * if any parameter type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any parameter type points to a type that cannot be
+ * instantiated for some reason
+ */
+ public Type[] getGenericParameterTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericParameterTypes);
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Type} instances. If
+ * this constructor has no declared exceptions, an empty array will be
+ * returned.
+ *
+ * @return an array of generic exception types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic constructor signature is invalid
+ * @throws TypeNotPresentException
+ * if any exception type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any exception type points to a type that cannot be
+ * instantiated for some reason
+ */
+ public Type[] getGenericExceptionTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericExceptionTypes);
+ }
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return Method.getDeclaredAnnotations(declaringClass, slot);
+ }
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return Method.getAnnotation(declaringClass, slot, annotationType);
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return Method.isAnnotationPresent(declaringClass, slot, annotationType);
+ }
+
+ /**
+ * Returns an array of arrays that represent the annotations of the formal
+ * parameters of this constructor. If there are no parameters on this
+ * constructor, then an empty array is returned. If there are no annotations
+ * set, then an array of empty arrays is returned.
+ *
+ * @return an array of arrays of {@code Annotation} instances
+ */
+ public Annotation[][] getParameterAnnotations() {
+ Annotation[][] parameterAnnotations
+ = Method.getParameterAnnotations(declaringClass, slot);
+ if (parameterAnnotations.length == 0) {
+ return Method.noAnnotations(parameterTypes.length);
+ }
+ return parameterAnnotations;
+ }
+
+ /**
+ * Indicates whether or not this constructor takes a variable number of
+ * arguments.
+ *
+ * @return {@code true} if a vararg is declare, otherwise
+ * {@code false}
+ */
+ public boolean isVarArgs() {
+ int mods = Method.getMethodModifiers(declaringClass, slot);
+ return (mods & Modifier.VARARGS) != 0;
+ }
+
+ /**
+ * Indicates whether or not this constructor is synthetic (artificially
+ * introduced by the compiler).
+ *
+ * @return {@code true} if this constructor is synthetic, {@code false}
+ * otherwise
+ */
+ public boolean isSynthetic() {
+ int mods = Method.getMethodModifiers(declaringClass, slot);
+ return (mods & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * Indicates whether or not the specified {@code object} is equal to this
+ * constructor. To be equal, the specified object must be an instance
+ * of {@code Constructor} with the same declaring class and parameter types
+ * as this constructor.
+ *
+ * @param object
+ * the object to compare
+ *
+ * @return {@code true} if the specified object is equal to this
+ * constructor, {@code false} otherwise
+ *
+ * @see #hashCode
+ */
+ @Override
+ public boolean equals(Object object) {
+ return object instanceof Constructor && toString().equals(object.toString());
+ }
+
+ /**
+ * Returns the class that declares this constructor.
+ *
+ * @return the declaring class
+ */
+ public Class<T> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Class} instances. If
+ * this constructor has no declared exceptions, an empty array will be
+ * returned.
+ *
+ * @return the declared exception classes
+ */
+ public Class<?>[] getExceptionTypes() {
+ if (exceptionTypes == null) {
+ return EmptyArray.CLASS;
+ }
+ return exceptionTypes.clone();
+ }
+
+ /**
+ * Returns the modifiers for this constructor. The {@link Modifier} class
+ * should be used to decode the result.
+ *
+ * @return the modifiers for this constructor
+ *
+ * @see Modifier
+ */
+ public int getModifiers() {
+ return Method.getMethodModifiers(declaringClass, slot);
+ }
+
+ /**
+ * Returns the name of this constructor.
+ *
+ * @return the name of this constructor
+ */
+ public String getName() {
+ return declaringClass.getName();
+ }
+
+ /**
+ * Returns an array of the {@code Class} objects associated with the
+ * parameter types of this constructor. If the constructor was declared with
+ * no parameters, an empty array will be returned.
+ *
+ * @return the parameter types
+ */
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes.clone();
+ }
+
+ /**
+ * Returns the constructor's signature in non-printable form. This is called
+ * (only) from IO native code and needed for deriving the serialVersionUID
+ * of the class
+ *
+ * @return the constructor's signature
+ */
+ @SuppressWarnings("unused")
+ private String getSignature() {
+ StringBuilder result = new StringBuilder();
+
+ result.append('(');
+ for (int i = 0; i < parameterTypes.length; i++) {
+ result.append(getSignature(parameterTypes[i]));
+ }
+ result.append(")V");
+
+ return result.toString();
+ }
+
+ /**
+ * Returns an integer hash code for this constructor. Constructors which are
+ * equal return the same value for this method. The hash code for a
+ * Constructor is the hash code of the name of the declaring class.
+ *
+ * @return the hash code
+ *
+ * @see #equals
+ */
+ @Override
+ public int hashCode() {
+ return declaringClass.getName().hashCode();
+ }
+
+ /**
+ * Returns a new instance of the declaring class, initialized by dynamically
+ * invoking the constructor represented by this {@code Constructor} object.
+ * This reproduces the effect of {@code new declaringClass(arg1, arg2, ... ,
+ * argN)} This method performs the following:
+ * <ul>
+ * <li>A new instance of the declaring class is created. If the declaring
+ * class cannot be instantiated (i.e. abstract class, an interface, an array
+ * type, or a primitive type) then an InstantiationException is thrown.</li>
+ * <li>If this Constructor object is enforcing access control (see
+ * {@link AccessibleObject}) and this constructor is not accessible from the
+ * current context, an IllegalAccessException is thrown.</li>
+ * <li>If the number of arguments passed and the number of parameters do not
+ * match, an IllegalArgumentException is thrown.</li>
+ * <li>For each argument passed:
+ * <ul>
+ * <li>If the corresponding parameter type is a primitive type, the argument
+ * is unboxed. If the unboxing fails, an IllegalArgumentException is
+ * thrown.</li>
+ * <li>If the resulting argument cannot be converted to the parameter type
+ * via a widening conversion, an IllegalArgumentException is thrown.</li>
+ * </ul>
+ * <li>The constructor represented by this {@code Constructor} object is
+ * then invoked. If an exception is thrown during the invocation, it is
+ * caught and wrapped in an InvocationTargetException. This exception is
+ * then thrown. If the invocation completes normally, the newly initialized
+ * object is returned.
+ * </ul>
+ *
+ * @param args
+ * the arguments to the constructor
+ *
+ * @return the new, initialized, object
+ *
+ * @exception InstantiationException
+ * if the class cannot be instantiated
+ * @exception IllegalAccessException
+ * if this constructor is not accessible
+ * @exception IllegalArgumentException
+ * if an incorrect number of arguments are passed, or an
+ * argument could not be converted by a widening conversion
+ * @exception InvocationTargetException
+ * if an exception was thrown by the invoked constructor
+ *
+ * @see AccessibleObject
+ */
+ public T newInstance(Object... args) throws InstantiationException, IllegalAccessException,
+ IllegalArgumentException, InvocationTargetException {
+ return constructNative (args, declaringClass, parameterTypes, slot, flag);
+ }
+
+ private native T constructNative(Object[] args, Class<T> declaringClass,
+ Class<?>[] parameterTypes, int slot,
+ boolean noAccessCheck) throws InstantiationException, IllegalAccessException,
+ InvocationTargetException;
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * constructor. The format of the string is:
+ *
+ * <ol>
+ * <li>modifiers (if any)
+ * <li>declaring class name
+ * <li>'('
+ * <li>parameter types, separated by ',' (if any)
+ * <li>')'
+ * <li>'throws' plus exception types, separated by ',' (if any)
+ * </ol>
+ *
+ * For example:
+ * {@code public String(byte[],String) throws UnsupportedEncodingException}
+ *
+ * @return a printable representation for this constructor
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+
+ if (result.length() != 0)
+ result.append(' ');
+ result.append(declaringClass.getName());
+ result.append("(");
+ result.append(toString(parameterTypes));
+ result.append(")");
+ if (exceptionTypes != null && exceptionTypes.length != 0) {
+ result.append(" throws ");
+ result.append(toString(exceptionTypes));
+ }
+
+ return result.toString();
+ }
+}
diff --git a/libdvm/src/main/java/java/lang/reflect/Field.java b/libdvm/src/main/java/java/lang/reflect/Field.java
new file mode 100644
index 0000000..0aacb11
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/reflect/Field.java
@@ -0,0 +1,934 @@
+/*
+ * 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.reflect;
+
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.Types;
+
+/**
+ * This class represents a field. Information about the field can be accessed,
+ * and the field's value can be accessed dynamically.
+ */
+public final class Field extends AccessibleObject implements Member {
+
+ /**
+ * Orders fields by their name and declaring class.
+ *
+ * @hide
+ */
+ public static final Comparator<Field> ORDER_BY_NAME_AND_DECLARING_CLASS
+ = new Comparator<Field>() {
+ @Override public int compare(Field a, Field b) {
+ int comparison = a.name.compareTo(b.name);
+ if (comparison != 0) {
+ return comparison;
+ }
+
+ return a.getDeclaringClass().getName().compareTo(b.getDeclaringClass().getName());
+ }
+ };
+
+ private Class<?> declaringClass;
+
+ private Class<?> type;
+
+ private Type genericType;
+
+ private volatile boolean genericTypesAreInitialized = false;
+
+ private String name;
+
+ private int slot;
+
+ private static final char TYPE_BOOLEAN = 'Z';
+
+ private static final char TYPE_BYTE = 'B';
+
+ private static final char TYPE_CHAR = 'C';
+
+ private static final char TYPE_SHORT = 'S';
+
+ private static final char TYPE_INTEGER = 'I';
+
+ private static final char TYPE_FLOAT = 'F';
+
+ private static final char TYPE_LONG = 'J';
+
+ private static final char TYPE_DOUBLE = 'D';
+
+ /**
+ * Construct a clone of the given instance.
+ *
+ * @param orig non-null; the original instance to clone
+ */
+ /*package*/ Field(Field orig) {
+ this(orig.declaringClass, orig.type, orig.name, orig.slot);
+
+ // Copy the accessible flag.
+ if (orig.flag) {
+ this.flag = true;
+ }
+ }
+
+ private Field(Class<?> declaringClass, Class<?> type, String name, int slot) {
+ this.declaringClass = declaringClass;
+ this.type = type;
+ this.name = name;
+ this.slot = slot;
+ }
+
+ private synchronized void initGenericType() {
+ if (!genericTypesAreInitialized) {
+ String signatureAttribute = getSignatureAttribute();
+ GenericSignatureParser parser = new GenericSignatureParser(
+ declaringClass.getClassLoader());
+ parser.parseForField(this.declaringClass, signatureAttribute);
+ genericType = parser.fieldType;
+ if (genericType == null) {
+ genericType = getType();
+ }
+ genericTypesAreInitialized = true;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ /* package */String getSignatureAttribute() {
+ Object[] annotation = getSignatureAnnotation(declaringClass, slot);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Get the Signature annotation for this field. Returns null if not found.
+ */
+ native private Object[] getSignatureAnnotation(Class declaringClass, int slot);
+
+ /**
+ * Indicates whether or not this field is synthetic.
+ *
+ * @return {@code true} if this field is synthetic, {@code false} otherwise
+ */
+ public boolean isSynthetic() {
+ int flags = getFieldModifiers(declaringClass, slot);
+ return (flags & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * Returns the string representation of this field, including the field's
+ * generic type.
+ *
+ * @return the string representation of this field
+ */
+ public String toGenericString() {
+ StringBuilder sb = new StringBuilder(80);
+ // append modifiers if any
+ int modifier = getModifiers();
+ if (modifier != 0) {
+ sb.append(Modifier.toString(modifier)).append(' ');
+ }
+ // append generic type
+ appendGenericType(sb, getGenericType());
+ sb.append(' ');
+ // append full field name
+ sb.append(getDeclaringClass().getName()).append('.').append(getName());
+ return sb.toString();
+ }
+
+ /**
+ * Indicates whether or not this field is an enumeration constant.
+ *
+ * @return {@code true} if this field is an enumeration constant, {@code
+ * false} otherwise
+ */
+ public boolean isEnumConstant() {
+ int flags = getFieldModifiers(declaringClass, slot);
+ return (flags & Modifier.ENUM) != 0;
+ }
+
+ /**
+ * Returns the generic type of this field.
+ *
+ * @return the generic type
+ * @throws GenericSignatureFormatError
+ * if the generic field signature is invalid
+ * @throws TypeNotPresentException
+ * if the generic type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if the generic type points to a type that cannot be
+ * instantiated for some reason
+ */
+ public Type getGenericType() {
+ initGenericType();
+ return Types.getType(genericType);
+ }
+
+ @Override public Annotation[] getDeclaredAnnotations() {
+ return getDeclaredAnnotations(declaringClass, slot);
+ }
+ private static native Annotation[] getDeclaredAnnotations(Class declaringClass, int slot);
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return getAnnotation(declaringClass, slot, annotationType);
+ }
+ private static native <A extends Annotation> A getAnnotation(
+ Class<?> declaringClass, int slot, Class<A> annotationType);
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return isAnnotationPresent(declaringClass, slot, annotationType);
+ }
+ private static native boolean isAnnotationPresent(
+ Class<?> declaringClass, int slot, Class<? extends Annotation> annotationType);
+
+ /**
+ * Indicates whether or not the specified {@code object} is equal to this
+ * field. To be equal, the specified object must be an instance of
+ * {@code Field} with the same declaring class, type and name as this field.
+ *
+ * @param object
+ * the object to compare
+ * @return {@code true} if the specified object is equal to this method,
+ * {@code false} otherwise
+ * @see #hashCode
+ */
+ @Override
+ public boolean equals(Object object) {
+ return object instanceof Field && toString().equals(object.toString());
+ }
+
+ /**
+ * Returns the value of the field in the specified object. This reproduces
+ * the effect of {@code object.fieldName}
+ *
+ * <p>If the type of this field is a primitive type, the field value is
+ * automatically boxed.
+ *
+ * <p>If this field is static, the object argument is ignored.
+ * Otherwise, if the object is null, a NullPointerException is thrown. If
+ * the object is not an instance of the declaring class of the method, an
+ * IllegalArgumentException is thrown.
+ *
+ * <p>If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value, possibly boxed
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public Object get(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getField(object, declaringClass, type, slot, flag);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code
+ * boolean}. This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public boolean getBoolean(Object object) throws IllegalAccessException,
+ IllegalArgumentException {
+ return getZField(object, declaringClass, type, slot, flag, TYPE_BOOLEAN);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code byte}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public byte getByte(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getBField(object, declaringClass, type, slot, flag, TYPE_BYTE);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code char}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public char getChar(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getCField(object, declaringClass, type, slot, flag, TYPE_CHAR);
+ }
+
+ /**
+ * Returns the class that declares this field.
+ *
+ * @return the declaring class
+ */
+ public Class<?> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code
+ * double}. This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public double getDouble(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getDField(object, declaringClass, type, slot, flag, TYPE_DOUBLE);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code float}
+ * . This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public float getFloat(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getFField(object, declaringClass, type, slot, flag, TYPE_FLOAT);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as an {@code int}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public int getInt(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getIField(object, declaringClass, type, slot, flag, TYPE_INTEGER);
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code long}.
+ * This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public long getLong(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getJField(object, declaringClass, type, slot, flag, TYPE_LONG);
+ }
+
+ /**
+ * Returns the modifiers for this field. The {@link Modifier} class should
+ * be used to decode the result.
+ *
+ * @return the modifiers for this field
+ * @see Modifier
+ */
+ public int getModifiers() {
+ return getFieldModifiers(declaringClass, slot);
+ }
+
+ private native int getFieldModifiers(Class<?> declaringClass, int slot);
+
+ /**
+ * Returns the name of this field.
+ *
+ * @return the name of this field
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the value of the field in the specified object as a {@code short}
+ * . This reproduces the effect of {@code object.fieldName}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @return the field value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public short getShort(Object object) throws IllegalAccessException, IllegalArgumentException {
+ return getSField(object, declaringClass, type, slot, flag, TYPE_SHORT);
+ }
+
+ /**
+ * Returns the constructor's signature in non-printable form. This is called
+ * (only) from IO native code and needed for deriving the serialVersionUID
+ * of the class
+ *
+ * @return the constructor's signature.
+ */
+ @SuppressWarnings("unused")
+ private String getSignature() {
+ return getSignature(type);
+ }
+
+ /**
+ * Return the {@link Class} associated with the type of this field.
+ *
+ * @return the type of this field
+ */
+ public Class<?> getType() {
+ return type;
+ }
+
+ /**
+ * Returns an integer hash code for this field. Objects which are equal
+ * return the same value for this method.
+ * <p>
+ * The hash code for a Field is the exclusive-or combination of the hash
+ * code of the field's name and the hash code of the name of its declaring
+ * class.
+ *
+ * @return the hash code for this field
+ * @see #equals
+ */
+ @Override
+ public int hashCode() {
+ return name.hashCode() ^ getDeclaringClass().getName().hashCode();
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the value. This
+ * reproduces the effect of {@code object.fieldName = value}
+ *
+ * <p>If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ *
+ * <p>If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ *
+ * <p>If the field type is a primitive type, the value is automatically
+ * unboxed. If the unboxing fails, an IllegalArgumentException is thrown. If
+ * the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void set(Object object, Object value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setField(object, declaringClass, type, slot, flag, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code
+ * boolean} value. This reproduces the effect of {@code object.fieldName =
+ * value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setBoolean(Object object, boolean value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setZField(object, declaringClass, type, slot, flag, TYPE_BOOLEAN, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code byte}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setByte(Object object, byte value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setBField(object, declaringClass, type, slot, flag, TYPE_BYTE, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code char}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setChar(Object object, char value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setCField(object, declaringClass, type, slot, flag, TYPE_CHAR, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code double}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setDouble(Object object, double value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setDField(object, declaringClass, type, slot, flag, TYPE_DOUBLE, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code float}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setFloat(Object object, float value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setFField(object, declaringClass, type, slot, flag, TYPE_FLOAT, value);
+ }
+
+ /**
+ * Set the value of the field in the specified object to the {@code int}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setInt(Object object, int value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setIField(object, declaringClass, type, slot, flag, TYPE_INTEGER, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code long}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setLong(Object object, long value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setJField(object, declaringClass, type, slot, flag, TYPE_LONG, value);
+ }
+
+ /**
+ * Sets the value of the field in the specified object to the {@code short}
+ * value. This reproduces the effect of {@code object.fieldName = value}
+ * <p>
+ * If this field is static, the object argument is ignored.
+ * Otherwise, if the object is {@code null}, a NullPointerException is
+ * thrown. If the object is not an instance of the declaring class of the
+ * method, an IllegalArgumentException is thrown.
+ * <p>
+ * If this Field object is enforcing access control (see AccessibleObject)
+ * and this field is not accessible from the current context, an
+ * IllegalAccessException is thrown.
+ * <p>
+ * If the value cannot be converted to the field type via a widening
+ * conversion, an IllegalArgumentException is thrown.
+ *
+ * @param object
+ * the object to access
+ * @param value
+ * the new value
+ * @throws NullPointerException
+ * if the object is {@code null} and the field is non-static
+ * @throws IllegalArgumentException
+ * if the object is not compatible with the declaring class
+ * @throws IllegalAccessException
+ * if this field is not accessible
+ */
+ public void setShort(Object object, short value) throws IllegalAccessException,
+ IllegalArgumentException {
+ setSField(object, declaringClass, type, slot, flag, TYPE_SHORT, value);
+ }
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * field.
+ * <p>
+ * The format of the string is:
+ * <ol>
+ * <li>modifiers (if any)
+ * <li>type
+ * <li>declaring class name
+ * <li>'.'
+ * <li>field name
+ * </ol>
+ * <p>
+ * For example: {@code public static java.io.InputStream
+ * java.lang.System.in}
+ *
+ * @return a printable representation for this field
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+ if (result.length() != 0) {
+ result.append(' ');
+ }
+ appendTypeName(result, type);
+ result.append(' ');
+ appendTypeName(result, declaringClass);
+ result.append('.');
+ result.append(name);
+ return result.toString();
+ }
+
+ private native Object getField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck) throws IllegalAccessException;
+
+ private native double getDField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native int getIField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native long getJField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native boolean getZField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native float getFField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native char getCField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native short getSField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native byte getBField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor) throws IllegalAccessException;
+
+ private native void setField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, Object value) throws IllegalAccessException;
+
+ private native void setDField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, double v) throws IllegalAccessException;
+
+ private native void setIField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, int i) throws IllegalAccessException;
+
+ private native void setJField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, long j) throws IllegalAccessException;
+
+ private native void setZField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, boolean z) throws IllegalAccessException;
+
+ private native void setFField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, float f) throws IllegalAccessException;
+
+ private native void setCField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, char c) throws IllegalAccessException;
+
+ private native void setSField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, short s) throws IllegalAccessException;
+
+ private native void setBField(Object o, Class<?> declaringClass, Class<?> type, int slot,
+ boolean noAccessCheck, char descriptor, byte b) throws IllegalAccessException;
+
+}
diff --git a/libdvm/src/main/java/java/lang/reflect/Method.java b/libdvm/src/main/java/java/lang/reflect/Method.java
new file mode 100644
index 0000000..b2f970a
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/reflect/Method.java
@@ -0,0 +1,597 @@
+/*
+ * 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.reflect;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.Comparator;
+import libcore.util.EmptyArray;
+import org.apache.harmony.kernel.vm.StringUtils;
+import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
+import org.apache.harmony.luni.lang.reflect.ListOfTypes;
+import org.apache.harmony.luni.lang.reflect.Types;
+
+/**
+ * This class represents a method. Information about the method can be accessed,
+ * and the method can be invoked dynamically.
+ */
+public final class Method extends AccessibleObject implements GenericDeclaration, Member {
+
+ /**
+ * Orders methods by their name, parameters and return type.
+ *
+ * @hide
+ */
+ public static final Comparator<Method> ORDER_BY_SIGNATURE = new Comparator<Method>() {
+ public int compare(Method a, Method b) {
+ int comparison = a.name.compareTo(b.name);
+ if (comparison != 0) {
+ return comparison;
+ }
+
+ Class<?>[] aParameters = a.parameterTypes;
+ Class<?>[] bParameters = b.parameterTypes;
+ int length = Math.min(aParameters.length, bParameters.length);
+ for (int i = 0; i < length; i++) {
+ comparison = aParameters[i].getName().compareTo(bParameters[i].getName());
+ if (comparison != 0) {
+ return comparison;
+ }
+ }
+
+ if (aParameters.length != bParameters.length) {
+ return aParameters.length - bParameters.length;
+ }
+
+ // this is necessary for methods that have covariant return types.
+ return a.getReturnType().getName().compareTo(b.getReturnType().getName());
+ }
+ };
+
+ private int slot;
+
+ private Class<?> declaringClass;
+
+ private String name;
+
+ private Class<?>[] parameterTypes;
+
+ private Class<?>[] exceptionTypes;
+
+ private Class<?> returnType;
+
+ private ListOfTypes genericExceptionTypes;
+ private ListOfTypes genericParameterTypes;
+ private Type genericReturnType;
+ private TypeVariable<Method>[] formalTypeParameters;
+ private volatile boolean genericTypesAreInitialized = false;
+
+ private synchronized void initGenericTypes() {
+ if (!genericTypesAreInitialized) {
+ String signatureAttribute = getSignatureAttribute();
+ GenericSignatureParser parser = new GenericSignatureParser(
+ declaringClass.getClassLoader());
+ parser.parseForMethod(this, signatureAttribute, exceptionTypes);
+ formalTypeParameters = parser.formalTypeParameters;
+ genericParameterTypes = parser.parameterTypes;
+ genericExceptionTypes = parser.exceptionTypes;
+ genericReturnType = parser.returnType;
+ genericTypesAreInitialized = true;
+ }
+ }
+
+ /**
+ * Construct a clone of the given instance.
+ *
+ * @param orig non-null; the original instance to clone
+ */
+ /*package*/ Method(Method orig) {
+ this(orig.declaringClass, orig.parameterTypes, orig.exceptionTypes,
+ orig.returnType, orig.name, orig.slot);
+
+ // Copy the accessible flag.
+ if (orig.flag) {
+ this.flag = true;
+ }
+ }
+
+ private Method(Class<?> declaring, Class<?>[] paramTypes, Class<?>[] exceptTypes, Class<?> returnType, String name, int slot)
+ {
+ this.declaringClass = declaring;
+ this.name = name;
+ this.slot = slot;
+ this.parameterTypes = paramTypes;
+ this.exceptionTypes = exceptTypes; // may be null
+ this.returnType = returnType;
+ }
+
+ public TypeVariable<Method>[] getTypeParameters() {
+ initGenericTypes();
+ return formalTypeParameters.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override /*package*/ String getSignatureAttribute() {
+ Object[] annotation = getSignatureAnnotation(declaringClass, slot);
+
+ if (annotation == null) {
+ return null;
+ }
+
+ return StringUtils.combineStrings(annotation);
+ }
+
+ /**
+ * Returns the Signature annotation for this method. Returns {@code null} if
+ * not found.
+ */
+ static native Object[] getSignatureAnnotation(Class declaringClass, int slot);
+
+ /**
+ * Returns the string representation of the method's declaration, including
+ * the type parameters.
+ *
+ * @return the string representation of this method
+ */
+ public String toGenericString() {
+ StringBuilder sb = new StringBuilder(80);
+
+ initGenericTypes();
+
+ // append modifiers if any
+ int modifier = getModifiers();
+ if (modifier != 0) {
+ sb.append(Modifier.toString(modifier & ~(Modifier.BRIDGE +
+ Modifier.VARARGS))).append(' ');
+ }
+ // append type parameters
+ if (formalTypeParameters != null && formalTypeParameters.length > 0) {
+ sb.append('<');
+ for (int i = 0; i < formalTypeParameters.length; i++) {
+ appendGenericType(sb, formalTypeParameters[i]);
+ if (i < formalTypeParameters.length - 1) {
+ sb.append(",");
+ }
+ }
+ sb.append("> ");
+ }
+ // append return type
+ appendGenericType(sb, Types.getType(genericReturnType));
+ sb.append(' ');
+ // append method name
+ appendTypeName(sb, getDeclaringClass());
+ sb.append(".").append(getName());
+ // append parameters
+ sb.append('(');
+ appendArrayGenericType(sb,
+ Types.getClonedTypeArray(genericParameterTypes));
+ sb.append(')');
+ // append exceptions if any
+ Type[] genericExceptionTypeArray = Types.getClonedTypeArray(
+ genericExceptionTypes);
+ if (genericExceptionTypeArray.length > 0) {
+ sb.append(" throws ");
+ appendArrayGenericType(sb, genericExceptionTypeArray);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the parameter types as an array of {@code Type} instances, in
+ * declaration order. If this method has no parameters, an empty array is
+ * returned.
+ *
+ * @return the parameter types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic method signature is invalid
+ * @throws TypeNotPresentException
+ * if any parameter type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any parameter type points to a type that cannot be
+ * instantiated for some reason
+ */
+ public Type[] getGenericParameterTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericParameterTypes);
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Type} instances. If
+ * this method has no declared exceptions, an empty array will be returned.
+ *
+ * @return an array of generic exception types
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic method signature is invalid
+ * @throws TypeNotPresentException
+ * if any exception type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if any exception type points to a type that cannot be
+ * instantiated for some reason
+ */
+ public Type[] getGenericExceptionTypes() {
+ initGenericTypes();
+ return Types.getClonedTypeArray(genericExceptionTypes);
+ }
+
+ /**
+ * Returns the return type of this method as a {@code Type} instance.
+ *
+ * @return the return type of this method
+ *
+ * @throws GenericSignatureFormatError
+ * if the generic method signature is invalid
+ * @throws TypeNotPresentException
+ * if the return type points to a missing type
+ * @throws MalformedParameterizedTypeException
+ * if the return type points to a type that cannot be
+ * instantiated for some reason
+ */
+ public Type getGenericReturnType() {
+ initGenericTypes();
+ return Types.getType(genericReturnType);
+ }
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return getDeclaredAnnotations(declaringClass, slot);
+ }
+ static native Annotation[] getDeclaredAnnotations(Class<?> declaringClass, int slot);
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return getAnnotation(declaringClass, slot, annotationType);
+ }
+ static native <A extends Annotation> A getAnnotation(
+ Class<?> declaringClass, int slot, Class<A> annotationType);
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return isAnnotationPresent(declaringClass, slot, annotationType);
+ }
+ static native boolean isAnnotationPresent(
+ Class<?> declaringClass, int slot, Class<? extends Annotation> annotationType);
+
+ private static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
+
+ /**
+ * Creates an array of empty Annotation arrays.
+ */
+ /*package*/ static Annotation[][] noAnnotations(int size) {
+ Annotation[][] annotations = new Annotation[size][];
+ for (int i = 0; i < size; i++) {
+ annotations[i] = NO_ANNOTATIONS;
+ }
+ return annotations;
+ }
+
+ /**
+ * Returns an array of arrays that represent the annotations of the formal
+ * parameters of this method. If there are no parameters on this method,
+ * then an empty array is returned. If there are no annotations set, then
+ * and array of empty arrays is returned.
+ *
+ * @return an array of arrays of {@code Annotation} instances
+ */
+ public Annotation[][] getParameterAnnotations() {
+ Annotation[][] parameterAnnotations
+ = getParameterAnnotations(declaringClass, slot);
+ if (parameterAnnotations.length == 0) {
+ return noAnnotations(parameterTypes.length);
+ }
+ return parameterAnnotations;
+ }
+
+ static native Annotation[][] getParameterAnnotations(Class declaringClass, int slot);
+
+ /**
+ * Indicates whether or not this method takes a variable number argument.
+ *
+ * @return {@code true} if a vararg is declared, {@code false} otherwise
+ */
+ public boolean isVarArgs() {
+ int modifiers = getMethodModifiers(declaringClass, slot);
+ return (modifiers & Modifier.VARARGS) != 0;
+ }
+
+ /**
+ * Indicates whether or not this method is a bridge.
+ *
+ * @return {@code true} if this method is a bridge, {@code false} otherwise
+ */
+ public boolean isBridge() {
+ int modifiers = getMethodModifiers(declaringClass, slot);
+ return (modifiers & Modifier.BRIDGE) != 0;
+ }
+
+ /**
+ * Indicates whether or not this method is synthetic.
+ *
+ * @return {@code true} if this method is synthetic, {@code false} otherwise
+ */
+ public boolean isSynthetic() {
+ int modifiers = getMethodModifiers(declaringClass, slot);
+ return (modifiers & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * Returns the default value for the annotation member represented by this
+ * method.
+ *
+ * @return the default value, or {@code null} if none
+ *
+ * @throws TypeNotPresentException
+ * if this annotation member is of type {@code Class} and no
+ * definition can be found
+ */
+ public Object getDefaultValue() {
+ return getDefaultValue(declaringClass, slot);
+ }
+ native private Object getDefaultValue(Class declaringClass, int slot);
+
+ /**
+ * Indicates whether or not the specified {@code object} is equal to this
+ * method. To be equal, the specified object must be an instance
+ * of {@code Method} with the same declaring class and parameter types
+ * as this method.
+ *
+ * @param object
+ * the object to compare
+ *
+ * @return {@code true} if the specified object is equal to this
+ * method, {@code false} otherwise
+ *
+ * @see #hashCode
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (!(object instanceof Method)) {
+ return false;
+ }
+ Method rhs = (Method) object;
+ // We don't compare exceptionTypes because two methods
+ // can't differ only by their declared exceptions.
+ return declaringClass.equals(rhs.declaringClass) &&
+ name.equals(rhs.name) &&
+ getModifiers() == rhs.getModifiers() &&
+ returnType.equals(rhs.returnType) &&
+ Arrays.equals(parameterTypes, rhs.parameterTypes);
+ }
+
+ /**
+ * Returns the class that declares this method.
+ *
+ * @return the declaring class
+ */
+ public Class<?> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Class} instances. If
+ * this method has no declared exceptions, an empty array is returned.
+ *
+ * @return the declared exception classes
+ */
+ public Class<?>[] getExceptionTypes() {
+ if (exceptionTypes == null) {
+ return EmptyArray.CLASS;
+ }
+ return exceptionTypes.clone();
+ }
+
+ /**
+ * Returns the modifiers for this method. The {@link Modifier} class should
+ * be used to decode the result.
+ *
+ * @return the modifiers for this method
+ *
+ * @see Modifier
+ */
+ public int getModifiers() {
+ return getMethodModifiers(declaringClass, slot);
+ }
+
+ static native int getMethodModifiers(Class<?> declaringClass, int slot);
+
+ /**
+ * Returns the name of the method represented by this {@code Method}
+ * instance.
+ *
+ * @return the name of this method
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns an array of {@code Class} objects associated with the parameter
+ * types of this method. If the method was declared with no parameters, an
+ * empty array will be returned.
+ *
+ * @return the parameter types
+ */
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes.clone();
+ }
+
+ /**
+ * Returns the {@code Class} associated with the return type of this
+ * method.
+ *
+ * @return the return type
+ */
+ public Class<?> getReturnType() {
+ return returnType;
+ }
+
+ /**
+ * Returns an integer hash code for this method. Objects which are equal
+ * return the same value for this method. The hash code for this Method is
+ * the hash code of the name of this method.
+ *
+ * @return hash code for this method
+ *
+ * @see #equals
+ */
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * Returns the result of dynamically invoking this method. Equivalent to
+ * {@code receiver.methodName(arg1, arg2, ... , argN)}.
+ *
+ * <p>If the method is static, the receiver argument is ignored (and may be null).
+ *
+ * <p>If the method takes no arguments, you can pass {@code (Object[]) null} instead of
+ * allocating an empty array.
+ *
+ * <p>If you're calling a varargs method, you need to pass an {@code Object[]} for the
+ * varargs parameter: that conversion is usually done in {@code javac}, not the VM, and
+ * the reflection machinery does not do this for you. (It couldn't, because it would be
+ * ambiguous.)
+ *
+ * <p>Reflective method invocation follows the usual process for method lookup.
+ *
+ * <p>If an exception is thrown during the invocation it is caught and
+ * wrapped in an InvocationTargetException. This exception is then thrown.
+ *
+ * <p>If the invocation completes normally, the return value itself is
+ * returned. If the method is declared to return a primitive type, the
+ * return value is boxed. If the return type is void, null is returned.
+ *
+ * @param receiver
+ * the object on which to call this method (or null for static methods)
+ * @param args
+ * the arguments to the method
+ * @return the result
+ *
+ * @throws NullPointerException
+ * if {@code receiver == null} for a non-static method
+ * @throws IllegalAccessException
+ * if this method is not accessible (see {@link AccessibleObject})
+ * @throws IllegalArgumentException
+ * if the number of arguments doesn't match the number of parameters, the receiver
+ * is incompatible with the declaring class, or an argument could not be unboxed
+ * or converted by a widening conversion to the corresponding parameter type
+ * @throws InvocationTargetException
+ * if an exception was thrown by the invoked method
+ */
+ public Object invoke(Object receiver, Object... args)
+ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ if (args == null) {
+ args = EmptyArray.OBJECT;
+ }
+ return invokeNative(receiver, args, declaringClass, parameterTypes, returnType, slot, flag);
+ }
+
+ private native Object invokeNative(Object obj, Object[] args, Class<?> declaringClass,
+ Class<?>[] parameterTypes, Class<?> returnType, int slot, boolean noAccessCheck)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException;
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * method. The format of the string is:
+ *
+ * <ol>
+ * <li>modifiers (if any)
+ * <li>return type or 'void'
+ * <li>declaring class name
+ * <li>'('
+ * <li>parameter types, separated by ',' (if any)
+ * <li>')'
+ * <li>'throws' plus exception types, separated by ',' (if any)
+ * </ol>
+ *
+ * For example: {@code public native Object
+ * java.lang.Method.invoke(Object,Object) throws
+ * IllegalAccessException,IllegalArgumentException
+ * ,InvocationTargetException}
+ *
+ * @return a printable representation for this method
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder(Modifier.toString(getModifiers()));
+
+ if (result.length() != 0)
+ result.append(' ');
+ result.append(returnType.getName());
+ result.append(' ');
+ result.append(declaringClass.getName());
+ result.append('.');
+ result.append(name);
+ result.append("(");
+ result.append(toString(parameterTypes));
+ result.append(")");
+ if (exceptionTypes != null && exceptionTypes.length != 0) {
+ result.append(" throws ");
+ result.append(toString(exceptionTypes));
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Returns the constructor's signature in non-printable form. This is called
+ * (only) from IO native code and needed for deriving the serialVersionUID
+ * of the class
+ *
+ * @return The constructor's signature.
+ */
+ @SuppressWarnings("unused")
+ private String getSignature() {
+ StringBuilder result = new StringBuilder();
+
+ result.append('(');
+ for (int i = 0; i < parameterTypes.length; i++) {
+ result.append(getSignature(parameterTypes[i]));
+ }
+ result.append(')');
+ result.append(getSignature(returnType));
+
+ return result.toString();
+ }
+
+}
diff --git a/libdvm/src/main/java/java/lang/reflect/Proxy.java b/libdvm/src/main/java/java/lang/reflect/Proxy.java
new file mode 100644
index 0000000..3b10887
--- /dev/null
+++ b/libdvm/src/main/java/java/lang/reflect/Proxy.java
@@ -0,0 +1,278 @@
+/*
+ * 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.reflect;
+
+import java.io.Serializable;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * {@code Proxy} defines methods for creating dynamic proxy classes and instances.
+ * A proxy class implements a declared set of interfaces and delegates method
+ * invocations to an {@code InvocationHandler}.
+ *
+ * @see InvocationHandler
+ * @since 1.3
+ */
+public class Proxy implements Serializable {
+
+ private static final long serialVersionUID = -2222568056686623797L;
+
+ // maps class loaders to created classes by interface names
+ private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> loaderCache = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>();
+
+ // to find previously created types
+ private static final Map<Class<?>, String> proxyCache = new WeakHashMap<Class<?>, String>();
+
+ private static int NextClassNameIndex = 0;
+
+ /**
+ * The invocation handler on which the method calls are dispatched.
+ */
+ protected InvocationHandler h;
+
+ @SuppressWarnings("unused")
+ private Proxy() {
+ }
+
+ /**
+ * Constructs a new {@code Proxy} instance with the specified invocation
+ * handler.
+ *
+ * @param h
+ * the invocation handler for the newly created proxy
+ */
+ protected Proxy(InvocationHandler h) {
+ this.h = h;
+ }
+
+ /**
+ * Returns the dynamically built {@code Class} for the specified interfaces.
+ * Creates a new {@code Class} when necessary. The order of the interfaces
+ * is relevant. Invocations of this method with the same interfaces but
+ * different order result in different generated classes. The interfaces
+ * must be visible from the supplied class loader; no duplicates are
+ * permitted. All non-public interfaces must be defined in the same package.
+ *
+ * @param loader
+ * the class loader that will define the proxy class
+ * @param interfaces
+ * an array of {@code Class} objects, each one identifying an
+ * interface that will be implemented by the returned proxy
+ * class
+ * @return a proxy class that implements all of the interfaces referred to
+ * in the contents of {@code interfaces}
+ * @throws IllegalArgumentException
+ * if any of the interface restrictions are violated
+ * @throws NullPointerException
+ * if either {@code interfaces} or any of its elements are
+ * {@code null}
+ */
+ public static Class<?> getProxyClass(ClassLoader loader,
+ Class<?>... interfaces) throws IllegalArgumentException {
+ // check that interfaces are a valid array of visible interfaces
+ if (interfaces == null) {
+ throw new NullPointerException("interfaces == null");
+ }
+ String commonPackageName = null;
+ for (int i = 0, length = interfaces.length; i < length; i++) {
+ Class<?> next = interfaces[i];
+ if (next == null) {
+ throw new NullPointerException("interfaces[" + i + "] == null");
+ }
+ String name = next.getName();
+ if (!next.isInterface()) {
+ throw new IllegalArgumentException(name + " is not an interface");
+ }
+ if (loader != next.getClassLoader()) {
+ try {
+ if (next != Class.forName(name, false, loader)) {
+ throw new IllegalArgumentException(name +
+ " is not visible from class loader");
+ }
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalArgumentException(name + " is not visible from class loader");
+ }
+ }
+ for (int j = i + 1; j < length; j++) {
+ if (next == interfaces[j]) {
+ throw new IllegalArgumentException(name + " appears more than once");
+ }
+ }
+ if (!Modifier.isPublic(next.getModifiers())) {
+ int last = name.lastIndexOf('.');
+ String p = last == -1 ? "" : name.substring(0, last);
+ if (commonPackageName == null) {
+ commonPackageName = p;
+ } else if (!commonPackageName.equals(p)) {
+ throw new IllegalArgumentException("non-public interfaces must be " +
+ "in the same package");
+ }
+ }
+ }
+
+ // search cache for matching proxy class using the class loader
+ synchronized (loaderCache) {
+ Map<String, WeakReference<Class<?>>> interfaceCache = loaderCache
+ .get(loader);
+ if (interfaceCache == null) {
+ loaderCache
+ .put(
+ loader,
+ (interfaceCache = new HashMap<String, WeakReference<Class<?>>>()));
+ }
+
+ String interfaceKey = "";
+ if (interfaces.length == 1) {
+ interfaceKey = interfaces[0].getName();
+ } else {
+ StringBuilder names = new StringBuilder();
+ for (int i = 0, length = interfaces.length; i < length; i++) {
+ names.append(interfaces[i].getName());
+ names.append(' ');
+ }
+ interfaceKey = names.toString();
+ }
+
+ Class<?> newClass;
+ WeakReference<Class<?>> ref = interfaceCache.get(interfaceKey);
+ if (ref == null) {
+ String nextClassName = "$Proxy" + NextClassNameIndex++;
+ if (commonPackageName != null && commonPackageName.length() > 0) {
+ nextClassName = commonPackageName + "." + nextClassName;
+ }
+ if (loader == null) {
+ loader = ClassLoader.getSystemClassLoader();
+ }
+ newClass = generateProxy(nextClassName.replace('.', '/'), interfaces, loader);
+ // Need a weak reference to the class so it can
+ // be unloaded if the class loader is discarded
+ interfaceCache.put(interfaceKey, new WeakReference<Class<?>>(newClass));
+ synchronized (proxyCache) {
+ // the value is unused
+ proxyCache.put(newClass, "");
+ }
+ } else {
+ newClass = ref.get();
+ assert newClass != null : "\ninterfaceKey=\"" + interfaceKey + "\""
+ + "\nloaderCache=\"" + loaderCache + "\""
+ + "\nintfCache=\"" + interfaceCache + "\""
+ + "\nproxyCache=\"" + proxyCache + "\"";
+ }
+ return newClass;
+ }
+ }
+
+ /**
+ * Returns an instance of the dynamically built class for the specified
+ * interfaces. Method invocations on the returned instance are forwarded to
+ * the specified invocation handler. The interfaces must be visible from the
+ * supplied class loader; no duplicates are permitted. All non-public
+ * interfaces must be defined in the same package.
+ *
+ * @param loader
+ * the class loader that will define the proxy class
+ * @param interfaces
+ * an array of {@code Class} objects, each one identifying an
+ * interface that will be implemented by the returned proxy
+ * object
+ * @param h
+ * the invocation handler that handles the dispatched method
+ * invocations
+ * @return a new proxy object that delegates to the handler {@code h}
+ * @throws IllegalArgumentException
+ * if any of the interface restrictions are violated
+ * @throws NullPointerException
+ * if the interfaces or any of its elements are null
+ */
+ public static Object newProxyInstance(ClassLoader loader,
+ Class<?>[] interfaces, InvocationHandler h)
+ throws IllegalArgumentException {
+ if (h == null) {
+ throw new NullPointerException("h == null");
+ }
+ try {
+ return getProxyClass(loader, interfaces).getConstructor(
+ new Class<?>[] { InvocationHandler.class }).newInstance(
+ new Object[] { h });
+ } catch (NoSuchMethodException ex) {
+ throw (InternalError) (new InternalError(ex.toString())
+ .initCause(ex));
+ } catch (IllegalAccessException ex) {
+ throw (InternalError) (new InternalError(ex.toString())
+ .initCause(ex));
+ } catch (InstantiationException ex) {
+ throw (InternalError) (new InternalError(ex.toString())
+ .initCause(ex));
+ } catch (InvocationTargetException ex) {
+ Throwable target = ex.getTargetException();
+ throw (InternalError) (new InternalError(target.toString())
+ .initCause(target));
+ }
+ }
+
+ /**
+ * Indicates whether or not the specified class is a dynamically generated
+ * proxy class.
+ *
+ * @param cl
+ * the class
+ * @return {@code true} if the class is a proxy class, {@code false}
+ * otherwise
+ * @throws NullPointerException
+ * if the class is {@code null}
+ */
+ public static boolean isProxyClass(Class<?> cl) {
+ if (cl == null) {
+ throw new NullPointerException("cl == null");
+ }
+ synchronized (proxyCache) {
+ return proxyCache.containsKey(cl);
+ }
+ }
+
+ /**
+ * Returns the invocation handler of the specified proxy instance.
+ *
+ * @param proxy
+ * the proxy instance
+ * @return the invocation handler of the specified proxy instance
+ * @throws IllegalArgumentException
+ * if the supplied {@code proxy} is not a proxy object
+ */
+ public static InvocationHandler getInvocationHandler(Object proxy)
+ throws IllegalArgumentException {
+
+ if (isProxyClass(proxy.getClass())) {
+ return ((Proxy) proxy).h;
+ }
+
+ throw new IllegalArgumentException("not a proxy instance");
+ }
+
+ native private static Class generateProxy(String name, Class[] interfaces,
+ ClassLoader loader);
+
+ /*
+ * The VM clones this method's descriptor when generating a proxy class.
+ * There is no implementation.
+ */
+ native private static void constructorPrototype(InvocationHandler h);
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java b/libdvm/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java
new file mode 100644
index 0000000..01d0e30
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.apache.harmony.kernel.vm;
+
+/**
+ * String utility functions.
+ */
+public final class StringUtils {
+ /**
+ * This class is uninstantiable.
+ */
+ private StringUtils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Combine a list of strings in an <code>Object[]</code> into a single
+ * string.
+ *
+ * @param list non-null; the strings to combine
+ * @return non-null; the combined form
+ */
+ public static String combineStrings(Object[] list) {
+ int listLength = list.length;
+
+ switch (listLength) {
+ case 0: {
+ return "";
+ }
+ case 1: {
+ return (String) list[0];
+ }
+ }
+
+ int strLength = 0;
+
+ for (int i = 0; i < listLength; i++) {
+ strLength += ((String) list[i]).length();
+ }
+
+ StringBuilder sb = new StringBuilder(strLength);
+
+ for (int i = 0; i < listLength; i++) {
+ sb.append(list[i]);
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java b/libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java
new file mode 100644
index 0000000..3fd13f5
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java
@@ -0,0 +1,317 @@
+/*
+ * 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 org.apache.harmony.lang.annotation;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.IncompleteAnnotationException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import static org.apache.harmony.lang.annotation.AnnotationMember.ARRAY;
+import static org.apache.harmony.lang.annotation.AnnotationMember.ERROR;
+
+/**
+ * The annotation implementation based on dynamically generated proxy instances.
+ * It conforms to all requirements stated in public APIs, see in particular
+ * {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement}
+ * and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}.
+ * Namely, annotation instances are immutable and serializable; they provide
+ * conforming access to annotation member values and required implementations of
+ * methods declared in Annotation interface.
+ *
+ * @see android.lang.annotation.AnnotationMember
+ * @see java.lang.annotation.Annotation
+ *
+ * @author Alexey V. Varlamov, Serguei S. Zapreyev
+ * @version $Revision$
+ */
+@SuppressWarnings({"serial"})
+public final class AnnotationFactory implements InvocationHandler, Serializable {
+
+ private static final transient
+ Map<Class<? extends Annotation>, AnnotationMember[]>
+ cache = new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>();
+
+ /**
+ * Reflects specified annotation type and returns an array
+ * of member element definitions with default values.
+ */
+ public static AnnotationMember[] getElementsDescription(Class<? extends Annotation> annotationType ) {
+ AnnotationMember[] desc = cache.get(annotationType);
+ if (desc == null) {
+ if (!annotationType.isAnnotation()) {
+ throw new IllegalArgumentException("Type is not annotation: "
+ + annotationType.getName());
+ }
+ Method[] m = annotationType.getDeclaredMethods();
+ desc = new AnnotationMember[m.length];
+ int idx = 0;
+ for (Method element : m) {
+ String name = element.getName();
+ Class<?> type = element.getReturnType();
+ try {
+ desc[idx] = new AnnotationMember(name,
+ element.getDefaultValue(), type, element);
+ } catch (Throwable t) {
+ desc[idx] = new AnnotationMember(name, t, type, element);
+ }
+ idx++;
+ }
+ cache.put(annotationType, desc);
+ }
+ return desc;
+ }
+
+ /**
+ * Provides a new annotation instance.
+ * @param annotationType the annotation type definition
+ * @param elements name-value pairs representing elements of the annotation
+ * @return a new annotation instance
+ */
+ public static Annotation createAnnotation(
+ Class<? extends Annotation> annotationType,
+ AnnotationMember[] elements)
+ {
+ AnnotationFactory antn = new AnnotationFactory(annotationType, elements);
+ return (Annotation)Proxy.newProxyInstance( annotationType.getClassLoader(),
+ new Class[]{annotationType}, antn);
+ }
+
+ private final Class<? extends Annotation> klazz;
+ private AnnotationMember[] elements;
+
+ /**
+ * New instances should not be created directly, use factory method
+ * {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()}
+ * instead.
+ *
+ * @param klzz class defining the annotation type
+ * @param values actual element values
+ */
+ private AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values) {
+ klazz = klzz;
+ AnnotationMember[] defs = getElementsDescription(klazz);
+ if (values == null) {
+ elements = defs;
+ } else {
+ //merge default and actual values
+ elements = new AnnotationMember[defs.length];
+ next: for (int i = elements.length - 1; i >= 0; i-- ){
+ for (AnnotationMember val : values){
+ if (val.name.equals(defs[i].name)) {
+ elements[i] = val.setDefinition(defs[i]);
+ continue next;
+ }
+ }
+ elements[i] = defs[i];
+ }
+ }
+ }
+
+ /**
+ * Reads the object, obtains actual member definitions for the annotation type,
+ * and merges deserialized values with the new definitions.
+ */
+ private void readObject(ObjectInputStream os) throws IOException,
+ ClassNotFoundException {
+ os.defaultReadObject();
+ // Annotation type members can be changed arbitrarily
+ // So there may be zombi elements from the previous life;
+ // they hardly fit into this new annotation's incarnation,
+ // as we have no defining methods for them.
+ // Reasonably just drop such elements,
+ // but seems better to keep them for compatibility
+ AnnotationMember[] defs = getElementsDescription(klazz);
+ AnnotationMember[] old = elements;
+ List<AnnotationMember> merged = new ArrayList<AnnotationMember>(
+ defs.length + old.length);
+ nextOld: for (AnnotationMember el1 : old) {
+ for (AnnotationMember el2 : defs) {
+ if (el2.name.equals(el1.name)) {
+ continue nextOld;
+ }
+ }
+ merged.add(el1); //phantom element
+ }
+ nextNew: for (AnnotationMember def : defs){
+ for (AnnotationMember val : old){
+ if (val.name.equals(def.name)) {
+ // nothing to do about cached errors (if any)
+ // anyway they remain relevant to values
+ merged.add(val.setDefinition(def));
+ continue nextNew;
+ }
+ }
+ merged.add(def); // brand new element
+ }
+ elements = merged.toArray(new AnnotationMember[merged.size()]);
+ }
+
+ /**
+ * Returns true if the specified object represents the same annotation instance.
+ * That is, if it implements the same annotation type and
+ * returns the same element values.
+ * <br>Note, actual underlying implementation mechanism does not matter - it may
+ * differ completely from this class.
+ * @return true if the passed object is equivalent annotation instance,
+ * false otherwise.
+ * @see android.lang.annotation.AnnotationMember#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!klazz.isInstance(obj)) {
+ return false;
+ }
+ Object handler = null;
+ if (Proxy.isProxyClass(obj.getClass())
+ && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory ) {
+ AnnotationFactory other = (AnnotationFactory) handler;
+ if (elements.length != other.elements.length) {
+ return false;
+ }
+ next: for (AnnotationMember el1 : elements){
+ for (AnnotationMember el2 : other.elements) {
+ if (el1.equals(el2)) {
+ continue next;
+ }
+ }
+ return false;
+ }
+ return true;
+ } else {
+ // encountered foreign annotation implementaton
+ // so have to obtain element values via invocation
+ // of corresponding methods
+ for (final AnnotationMember el : elements) {
+ if (el.tag == ERROR) {
+ // undefined value is incomparable (transcendent)
+ return false;
+ }
+ try {
+ if (!el.definingMethod.isAccessible()) {
+ el.definingMethod.setAccessible(true);
+ }
+ Object otherValue = el.definingMethod.invoke(obj);
+ if (otherValue != null ) {
+ if (el.tag == ARRAY) {
+ if (!el.equalArrayValue(otherValue)) {
+ return false;
+ }
+ } else {
+ if (!el.value.equals(otherValue)) {
+ return false;
+ }
+ }
+ } else if (el.value != AnnotationMember.NO_VALUE) {
+ return false;
+ }
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Returns a hash code composed as a sum of hash codes of member elements,
+ * including elements with default values.
+ * @see android.lang.annotation.AnnotationMember#hashCode()
+ */
+ public int hashCode() {
+ int hash = 0;
+ for (AnnotationMember element : elements) {
+ hash += element.hashCode();
+ }
+ return hash;
+ }
+
+ /**
+ * Provides detailed description of this annotation instance,
+ * including all member name-values pairs.
+ * @return string representation of this annotation
+ */
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append('@');
+ result.append(klazz.getName());
+ result.append('(');
+ for (int i = 0; i < elements.length; ++i) {
+ if (i != 0) {
+ result.append(", ");
+ }
+ result.append(elements[i]);
+ }
+ result.append(')');
+ return result.toString();
+ }
+
+ /**
+ * Processes a method invocation request to this annotation instance.
+ * Recognizes the methods declared in the
+ * {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}
+ * interface, and member-defining methods of the implemented annotation type.
+ * @throws IllegalArgumentException If the specified method is none of the above
+ * @return the invocation result
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ String name = method.getName();
+ Class[] params = method.getParameterTypes();
+ if (params.length == 0) {
+ if ("annotationType".equals(name)) {
+ return klazz;
+ } else if ("toString".equals(name)) {
+ return toString();
+ } else if ("hashCode".equals(name)) {
+ return hashCode();
+ }
+
+ // this must be element value request
+ AnnotationMember element = null;
+ for (AnnotationMember el : elements) {
+ if (name.equals(el.name)) {
+ element = el;
+ break;
+ }
+ }
+ if (element == null || !method.equals(element.definingMethod)) {
+ throw new IllegalArgumentException(method.toString());
+ } else {
+ Object value = element.validateValue();
+ if (value == null) {
+ throw new IncompleteAnnotationException(klazz, name);
+ }
+ return value;
+ }
+ } else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)){
+ return Boolean.valueOf(equals(args[0]));
+ }
+ throw new IllegalArgumentException(
+ "Invalid method for annotation type: " + method);
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java b/libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java
new file mode 100644
index 0000000..f64332a
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java
@@ -0,0 +1,387 @@
+/*
+ * 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 org.apache.harmony.lang.annotation;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.annotation.AnnotationTypeMismatchException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * This class represents member element of an annotation.
+ * It consists of name and value, supplemented with element
+ * definition information (such as declared type of element).
+ * <br>The value may be one of the following types:
+ * <ul>
+ * <li> boxed primitive
+ * <li> Class
+ * <li> enum constant
+ * <li> annotation (nested)
+ * <li> one-dimensional array of the above
+ * <li> Throwable
+ * </ul>
+ * The last type is specific for this implementation; a Throwable value
+ * means that the error occured during parsing or resolution of corresponding
+ * class-data structures and throwing is delayed until the element
+ * is requested for value.
+ *
+ * @see android.lang.annotation.AnnotationFactory
+ *
+ * @author Alexey V. Varlamov, Serguei S. Zapreyev
+ * @version $Revision$
+ */
+@SuppressWarnings({"serial"})
+public class AnnotationMember implements Serializable {
+
+ /**
+ * Tag description of a Throwable value type.
+ */
+ protected static final char ERROR = '!';
+
+ /**
+ * Tag description of an array value type.
+ */
+ protected static final char ARRAY = '[';
+
+ /**
+ * Tag description of all value types except arrays and Throwables.
+ */
+ protected static final char OTHER = '*';
+
+// public static final char INT = 'I';
+// public static final char CHAR = 'C';
+// public static final char DOUBLE = 'D';
+// public static final char FLOAT = 'F';
+// public static final char BYTE = 'B';
+// public static final char LONG = 'J';
+// public static final char SHORT = 'S';
+// public static final char BOOL = 'Z';
+// public static final char CLASS = 'c';
+// public static final char ENUM = 'e';
+// public static final char ANTN = '@';
+
+ private enum DefaultValues {NO_VALUE}
+
+ /**
+ * Singleton representing missing element value.
+ */
+ protected static final Object NO_VALUE = DefaultValues.NO_VALUE;
+
+ protected final String name;
+ protected final Object value; // a primitive value is wrapped to the corresponding wrapper class
+ protected final char tag;
+ // no sense to serialize definition info as it can be changed arbitrarily
+ protected transient Class<?> elementType;
+ protected transient Method definingMethod;
+
+
+ /**
+ * Creates a new element with specified name and value.
+ * Definition info will be provided later when this
+ * element becomes actual annotation member.
+ * @param name element name, must not be null
+ * @param val element value, should be of addmissible type,
+ * as specified in the description of this class
+ *
+ * @see #setDefinition(AnnotationMember)
+ */
+ public AnnotationMember(String name, Object val) {
+ this.name = name;
+ value = val == null ? NO_VALUE : val;
+ if (value instanceof Throwable) {
+ tag = ERROR;
+ } else if (value.getClass().isArray()) {
+ tag = ARRAY;
+ } else {
+ tag = OTHER;
+ }
+ }
+
+ /**
+ * Creates the completely defined element.
+ * @param name element name, must not be null
+ * @param value element value, should be of addmissible type,
+ * as specified in the description of this class
+ * @param m element-defining method, reflected on the annotation type
+ * @param type declared type of this element
+ * (return type of the defining method)
+ */
+ public AnnotationMember(String name, Object val, Class type, Method m) {
+ this(name, val);
+
+ definingMethod = m;
+
+ if (type == int.class) {
+ elementType = Integer.class;
+ } else if (type == boolean.class) {
+ elementType = Boolean.class;
+ } else if (type == char.class) {
+ elementType = Character.class;
+ } else if (type == float.class) {
+ elementType = Float.class;
+ } else if (type == double.class) {
+ elementType = Double.class;
+ } else if (type == long.class) {
+ elementType = Long.class;
+ } else if (type == short.class) {
+ elementType = Short.class;
+ } else if (type == byte.class) {
+ elementType = Byte.class;
+ } else {
+ elementType = type;
+ }
+ }
+
+ /**
+ * Fills in element's definition info and returns this.
+ */
+ protected AnnotationMember setDefinition(AnnotationMember copy) {
+ definingMethod = copy.definingMethod;
+ elementType = copy.elementType;
+ return this;
+ }
+
+ /**
+ * Returns readable description of this annotation value.
+ */
+ public String toString() {
+ if (tag == ARRAY) {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append(name).append("=[");
+ int len = Array.getLength(value);
+ for (int i = 0; i < len; i++) {
+ if (i != 0) sb.append(", ");
+ sb.append(Array.get(value, i));
+ }
+ return sb.append("]").toString();
+ } else {
+ return name+ "=" +value;
+ }
+ }
+
+ /**
+ * Returns true if the specified object represents equal element
+ * (equivalent name-value pair).
+ * <br> A special case is the contained Throwable value; it is considered
+ * transcendent so no other element would be equal.
+ * @return true if passed object is equivalent element representation,
+ * false otherwise
+ * @see #equalArrayValue(Object)
+ * @see java.lang.annotation.Annotation#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ // not a mere optimization,
+ // this is needed for consistency with hashCode()
+ return true;
+ }
+ if (obj instanceof AnnotationMember) {
+ AnnotationMember that = (AnnotationMember)obj;
+ if (name.equals(that.name) && tag == that.tag) {
+ if (tag == ARRAY) {
+ return equalArrayValue(that.value);
+ } else if (tag == ERROR) {
+ // undefined value is incomparable (transcendent)
+ return false;
+ } else {
+ return value.equals(that.value);
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the contained value and a passed object are equal arrays,
+ * false otherwise. Appropriate overloaded method of Arrays.equals()
+ * is used for equality testing.
+ * @see java.util.Arrays#equals(java.lang.Object[], java.lang.Object[])
+ * @return true if the value is array and is equal to specified object,
+ * false otherwise
+ */
+ public boolean equalArrayValue(Object otherValue) {
+ if (value instanceof Object[] && otherValue instanceof Object[]) {
+ return Arrays.equals((Object[])value, (Object[])otherValue);
+ }
+ Class type = value.getClass();
+ if (type != otherValue.getClass()) {
+ return false;
+ }
+ if (type == int[].class) {
+ return Arrays.equals((int[])value, (int[])otherValue);
+ } else if (type == byte[].class) {
+ return Arrays.equals((byte[])value, (byte[])otherValue);
+ } else if (type == short[].class) {
+ return Arrays.equals((short[])value, (short[])otherValue);
+ } else if (type == long[].class) {
+ return Arrays.equals((long[])value, (long[])otherValue);
+ } else if (type == char[].class) {
+ return Arrays.equals((char[])value, (char[])otherValue);
+ } else if (type == boolean[].class) {
+ return Arrays.equals((boolean[])value, (boolean[])otherValue);
+ } else if (type == float[].class) {
+ return Arrays.equals((float[])value, (float[])otherValue);
+ } else if (type == double[].class) {
+ return Arrays.equals((double[])value, (double[])otherValue);
+ }
+ return false;
+ }
+
+ /**
+ * Computes hash code of this element. The formula is as follows:
+ * <code> (name.hashCode() * 127) ^ value.hashCode() </code>
+ * <br>If value is an array, one of overloaded Arrays.hashCode()
+ * methods is used.
+ * @return the hash code
+ * @see java.util.Arrays#hashCode(java.lang.Object[])
+ * @see java.lang.annotation.Annotation#hashCode()
+ */
+ public int hashCode() {
+ int hash = name.hashCode() * 127;
+ if (tag == ARRAY) {
+ Class type = value.getClass();
+ if (type == int[].class) {
+ return hash ^ Arrays.hashCode((int[])value);
+ } else if (type == byte[].class) {
+ return hash ^ Arrays.hashCode((byte[])value);
+ } else if (type == short[].class) {
+ return hash ^ Arrays.hashCode((short[])value);
+ } else if (type == long[].class) {
+ return hash ^ Arrays.hashCode((long[])value);
+ } else if (type == char[].class) {
+ return hash ^ Arrays.hashCode((char[])value);
+ } else if (type == boolean[].class) {
+ return hash ^ Arrays.hashCode((boolean[])value);
+ } else if (type == float[].class) {
+ return hash ^ Arrays.hashCode((float[])value);
+ } else if (type == double[].class) {
+ return hash ^ Arrays.hashCode((double[])value);
+ }
+ return hash ^ Arrays.hashCode((Object[])value);
+ } else {
+ return hash ^ value.hashCode();
+ }
+ }
+
+ /**
+ * Throws contained error (if any) with a renewed stack trace.
+ */
+ public void rethrowError() throws Throwable {
+ if (tag == ERROR) {
+ // need to throw cloned exception for thread safety
+ // besides it is better to provide actual stack trace
+ // rather than recorded during parsing
+
+ // first check for expected types
+ if (value instanceof TypeNotPresentException) {
+ TypeNotPresentException tnpe = (TypeNotPresentException)value;
+ throw new TypeNotPresentException(tnpe.typeName(), tnpe.getCause());
+ } else if (value instanceof EnumConstantNotPresentException) {
+ EnumConstantNotPresentException ecnpe = (EnumConstantNotPresentException)value;
+ throw new EnumConstantNotPresentException(ecnpe.enumType(), ecnpe.constantName());
+ } else if (value instanceof ArrayStoreException) {
+ ArrayStoreException ase = (ArrayStoreException)value;
+ throw new ArrayStoreException(ase.getMessage());
+ }
+ // got some other error, have to go with deep cloning
+ // via serialization mechanism
+ Throwable error = (Throwable)value;
+ StackTraceElement[] ste = error.getStackTrace();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(
+ ste == null ? 512 : (ste.length + 1) * 80);
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(error);
+ oos.flush();
+ oos.close();
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos
+ .toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ error = (Throwable)ois.readObject();
+ ois.close();
+
+ throw error;
+ }
+ }
+
+ /**
+ * Validates contained value against its member definition
+ * and if ok returns the value.
+ * Otherwise, if the value type mismatches definition
+ * or the value itself describes an error,
+ * throws appropriate exception.
+ * <br> Note, this method may return null if this element was constructed
+ * with such value.
+ *
+ * @see #rethrowError()
+ * @see #copyValue()
+ * @return actual valid value or null if no value
+ */
+ public Object validateValue() throws Throwable {
+ if (tag == ERROR) {
+ rethrowError();
+ }
+ if (value == NO_VALUE) {
+ return null;
+ }
+ if (elementType == value.getClass()
+ || elementType.isInstance(value)) { // nested annotation value
+ return copyValue();
+ } else {
+ throw new AnnotationTypeMismatchException(definingMethod,
+ value.getClass().getName());
+ }
+
+ }
+
+
+ /**
+ * Provides mutation-safe access to contained value. That is, caller is free
+ * to modify the returned value, it will not affect the contained data value.
+ * @return cloned value if it is mutable or the original immutable value
+ */
+ public Object copyValue() throws Throwable
+ {
+ if (tag != ARRAY || Array.getLength(value) == 0) {
+ return value;
+ }
+ Class type = value.getClass();
+ if (type == int[].class) {
+ return ((int[])value).clone();
+ } else if (type == byte[].class) {
+ return ((byte[])value).clone();
+ } else if (type == short[].class) {
+ return ((short[])value).clone();
+ } else if (type == long[].class) {
+ return ((long[])value).clone();
+ } else if (type == char[].class) {
+ return ((char[])value).clone();
+ } else if (type == boolean[].class) {
+ return ((boolean[])value).clone();
+ } else if (type == float[].class) {
+ return ((float[])value).clone();
+ } else if (type == double[].class) {
+ return ((double[])value).clone();
+ }
+ return ((Object[])value).clone();
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/GenericSignatureParser.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/GenericSignatureParser.java
new file mode 100644
index 0000000..490daaf
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/GenericSignatureParser.java
@@ -0,0 +1,508 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.GenericSignatureFormatError;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+/**
+ * Implements a parser for the generics signature attribute.
+ * Uses a top-down, recursive descent parsing approach for the following grammar:
+ * <pre>
+ * ClassSignature ::=
+ * OptFormalTypeParams SuperclassSignature {SuperinterfaceSignature}.
+ * SuperclassSignature ::= ClassTypeSignature.
+ * SuperinterfaceSignature ::= ClassTypeSignature.
+ *
+ * OptFormalTypeParams ::=
+ * ["<" FormalTypeParameter {FormalTypeParameter} ">"].
+ *
+ * FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
+ * ClassBound ::= ":" [FieldTypeSignature].
+ * InterfaceBound ::= ":" FieldTypeSignature.
+ *
+ * FieldTypeSignature ::=
+ * ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
+ * ArrayTypeSignature ::= "[" TypSignature.
+ *
+ * ClassTypeSignature ::=
+ * "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} ";".
+ *
+ * OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
+ *
+ * TypeArgument ::= ([WildcardIndicator] FieldTypeSignature) | "*".
+ * WildcardIndicator ::= "+" | "-".
+ *
+ * TypeVariableSignature ::= "T" Ident ";".
+ *
+ * TypSignature ::= FieldTypeSignature | BaseType.
+ * BaseType ::= "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z".
+ *
+ * MethodTypeSignature ::=
+ * OptFormalTypeParams "(" {TypeSignature} ")" ReturnType {ThrowsSignature}.
+ * ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
+ *
+ * ReturnType ::= TypSignature | VoidDescriptor.
+ * VoidDescriptor ::= "V".
+ * </pre>
+ */
+public class GenericSignatureParser {
+
+ public ListOfTypes exceptionTypes;
+ public ListOfTypes parameterTypes;
+ public TypeVariable[] formalTypeParameters;
+ public Type returnType;
+ public Type fieldType;
+ public ListOfTypes interfaceTypes;
+ public Type superclassType;
+ public ClassLoader loader;
+
+ GenericDeclaration genericDecl;
+
+ /*
+ * Parser:
+ */
+ char symbol; // 0: eof; else valid term symbol or first char of identifier.
+ String identifier;
+
+
+ /*
+ * Scanner:
+ * eof is private to the scan methods
+ * and it's set only when a scan is issued at the end of the buffer.
+ */
+ private boolean eof;
+
+ char[] buffer;
+ int pos;
+
+ public GenericSignatureParser(ClassLoader loader) {
+ this.loader = loader;
+ }
+
+ void setInput(GenericDeclaration genericDecl, String input) {
+ if (input != null) {
+ this.genericDecl = genericDecl;
+ this.buffer = input.toCharArray();
+ this.eof = false;
+ scanSymbol();
+ }
+ else {
+ this.eof = true;
+ }
+ }
+
+ /**
+ * Parses the generic signature of a class and creates the data structure
+ * representing the signature.
+ *
+ * @param genericDecl the GenericDeclaration calling this method
+ * @param signature the generic signature of the class
+ */
+ public void parseForClass(GenericDeclaration genericDecl,
+ String signature) {
+ setInput(genericDecl, signature);
+ if (!eof) {
+ parseClassSignature();
+ } else {
+ if(genericDecl instanceof Class) {
+ Class c = (Class) genericDecl;
+ this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.superclassType = c.getSuperclass();
+ this.interfaceTypes = new ListOfTypes(c.getInterfaces());
+ } else {
+ this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.superclassType = Object.class;
+ this.interfaceTypes = ListOfTypes.EMPTY;
+ }
+ }
+ }
+
+ /**
+ * Parses the generic signature of a method and creates the data structure
+ * representing the signature.
+ *
+ * @param genericDecl the GenericDeclaration calling this method
+ * @param signature the generic signature of the class
+ */
+ public void parseForMethod(GenericDeclaration genericDecl,
+ String signature, Class<?>[] rawExceptionTypes) {
+ setInput(genericDecl, signature);
+ if (!eof) {
+ parseMethodTypeSignature(rawExceptionTypes);
+ } else {
+ if(genericDecl instanceof Method) {
+ Method m = (Method) genericDecl;
+ this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.parameterTypes = new ListOfTypes(m.getParameterTypes());
+ this.exceptionTypes = new ListOfTypes(m.getExceptionTypes());
+ this.returnType = m.getReturnType();
+ } else {
+ this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.parameterTypes = ListOfTypes.EMPTY;
+ this.exceptionTypes = ListOfTypes.EMPTY;
+ this.returnType = void.class;
+ }
+ }
+ }
+
+ /**
+ * Parses the generic signature of a constructor and creates the data
+ * structure representing the signature.
+ *
+ * @param genericDecl the GenericDeclaration calling this method
+ * @param signature the generic signature of the class
+ */
+ public void parseForConstructor(GenericDeclaration genericDecl,
+ String signature, Class<?>[] rawExceptionTypes) {
+ setInput(genericDecl, signature);
+ if (!eof) {
+ parseMethodTypeSignature(rawExceptionTypes);
+ } else {
+ if(genericDecl instanceof Constructor) {
+ Constructor c = (Constructor) genericDecl;
+ this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.parameterTypes = new ListOfTypes(c.getParameterTypes());
+ this.exceptionTypes = new ListOfTypes(c.getExceptionTypes());
+ } else {
+ this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.parameterTypes = ListOfTypes.EMPTY;
+ this.exceptionTypes = ListOfTypes.EMPTY;
+ }
+ }
+ }
+
+ /**
+ * Parses the generic signature of a field and creates the data structure
+ * representing the signature.
+ *
+ * @param genericDecl the GenericDeclaration calling this method
+ * @param signature the generic signature of the class
+ */
+ public void parseForField(GenericDeclaration genericDecl,
+ String signature) {
+ setInput(genericDecl, signature);
+ if (!eof) {
+ this.fieldType = parseFieldTypeSignature();
+ }
+ }
+
+
+ //
+ // Parser:
+ //
+
+ void parseClassSignature() {
+ // ClassSignature ::=
+ // OptFormalTypeParameters SuperclassSignature {SuperinterfaceSignature}.
+
+ parseOptFormalTypeParameters();
+
+ // SuperclassSignature ::= ClassTypeSignature.
+ this.superclassType = parseClassTypeSignature();
+
+ interfaceTypes = new ListOfTypes(16);
+ while (symbol > 0) {
+ // SuperinterfaceSignature ::= ClassTypeSignature.
+ interfaceTypes.add(parseClassTypeSignature());
+ }
+ }
+
+ void parseOptFormalTypeParameters() {
+ // OptFormalTypeParameters ::=
+ // ["<" FormalTypeParameter {FormalTypeParameter} ">"].
+
+ ListOfVariables typeParams = new ListOfVariables();
+
+ if (symbol == '<') {
+ scanSymbol();
+ typeParams.add(parseFormalTypeParameter());
+ while ((symbol != '>') && (symbol > 0)) {
+ typeParams.add(parseFormalTypeParameter());
+ }
+ expect('>');
+ }
+ this.formalTypeParameters = typeParams.getArray();
+ }
+
+ ImplForVariable<GenericDeclaration> parseFormalTypeParameter() {
+ // FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
+
+ scanIdentifier();
+ String name = identifier.intern(); // FIXME: is this o.k.?
+
+ ListOfTypes bounds = new ListOfTypes(8);
+
+ // ClassBound ::= ":" [FieldTypeSignature].
+ expect(':');
+ if (symbol == 'L' || symbol == '[' || symbol == 'T') {
+ bounds.add(parseFieldTypeSignature());
+ }
+
+ while (symbol == ':') {
+ // InterfaceBound ::= ":" FieldTypeSignature.
+ scanSymbol();
+ bounds.add(parseFieldTypeSignature());
+ }
+
+ return new ImplForVariable<GenericDeclaration>(genericDecl, name, bounds);
+ }
+
+ Type parseFieldTypeSignature() {
+ // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature
+ // | TypeVariableSignature.
+
+ switch (symbol) {
+ case 'L':
+ return parseClassTypeSignature();
+ case '[':
+ // ArrayTypeSignature ::= "[" TypSignature.
+ scanSymbol();
+ return new ImplForArray(parseTypeSignature());
+ case 'T':
+ return parseTypeVariableSignature();
+ default:
+ throw new GenericSignatureFormatError();
+ }
+ }
+
+ Type parseClassTypeSignature() {
+ // ClassTypeSignature ::= "L" {Ident "/"} Ident
+ // OptTypeArguments {"." Ident OptTypeArguments} ";".
+
+ expect('L');
+
+ StringBuilder qualIdent = new StringBuilder();
+ scanIdentifier();
+ while (symbol == '/') {
+ scanSymbol();
+ qualIdent.append(identifier).append(".");
+ scanIdentifier();
+ }
+
+ qualIdent.append(this.identifier);
+
+ ListOfTypes typeArgs = parseOptTypeArguments();
+ ImplForType parentType =
+ new ImplForType(null, qualIdent.toString(), typeArgs, loader);
+ ImplForType type = parentType;
+
+ while (symbol == '.') {
+ // Deal with Member Classes:
+ scanSymbol();
+ scanIdentifier();
+ qualIdent.append("$").append(identifier); // FIXME: is "$" correct?
+ typeArgs = parseOptTypeArguments();
+ type = new ImplForType(parentType, qualIdent.toString(), typeArgs,
+ loader);
+ }
+
+ expect(';');
+
+ return type;
+ }
+
+ ListOfTypes parseOptTypeArguments() {
+ // OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
+
+ ListOfTypes typeArgs = new ListOfTypes(8);
+ if (symbol == '<') {
+ scanSymbol();
+
+ typeArgs.add(parseTypeArgument());
+ while ((symbol != '>') && (symbol > 0)) {
+ typeArgs.add(parseTypeArgument());
+ }
+ expect('>');
+ }
+ return typeArgs;
+ }
+
+ Type parseTypeArgument() {
+ // TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*".
+ ListOfTypes extendsBound = new ListOfTypes(1);
+ ListOfTypes superBound = new ListOfTypes(1);
+ if (symbol == '*') {
+ scanSymbol();
+ extendsBound.add(Object.class);
+ return new ImplForWildcard(extendsBound, superBound);
+ }
+ else if (symbol == '+') {
+ scanSymbol();
+ extendsBound.add(parseFieldTypeSignature());
+ return new ImplForWildcard(extendsBound, superBound);
+ }
+ else if (symbol == '-') {
+ scanSymbol();
+ superBound.add(parseFieldTypeSignature());
+ extendsBound.add(Object.class);
+ return new ImplForWildcard(extendsBound, superBound);
+ }
+ else {
+ return parseFieldTypeSignature();
+ }
+ }
+
+ ImplForVariable<GenericDeclaration> parseTypeVariableSignature() {
+ // TypeVariableSignature ::= "T" Ident ";".
+ expect('T');
+ scanIdentifier();
+ expect(';');
+ // Reference to type variable:
+ // Note: we don't know the declaring GenericDeclaration yet.
+ return new ImplForVariable<GenericDeclaration>(genericDecl, identifier);
+ }
+
+ Type parseTypeSignature() {
+ switch (symbol) {
+ case 'B': scanSymbol(); return byte.class;
+ case 'C': scanSymbol(); return char.class;
+ case 'D': scanSymbol(); return double.class;
+ case 'F': scanSymbol(); return float.class;
+ case 'I': scanSymbol(); return int.class;
+ case 'J': scanSymbol(); return long.class;
+ case 'S': scanSymbol(); return short.class;
+ case 'Z': scanSymbol(); return boolean.class;
+ default:
+ // Not an elementary type, but a FieldTypeSignature.
+ return parseFieldTypeSignature();
+ }
+ }
+
+ /**
+ * @param rawExceptionTypes the non-generic exceptions. This is necessary
+ * because the signature may omit the exceptions when none are generic.
+ * May be null for methods that declare no exceptions.
+ */
+ void parseMethodTypeSignature(Class<?>[] rawExceptionTypes) {
+ // MethodTypeSignature ::= [FormalTypeParameters]
+ // "(" {TypeSignature} ")" ReturnType {ThrowsSignature}.
+
+ parseOptFormalTypeParameters();
+
+ parameterTypes = new ListOfTypes(16);
+ expect('(');
+ while (symbol != ')' && (symbol > 0)) {
+ parameterTypes.add(parseTypeSignature());
+ }
+ expect(')');
+
+ returnType = parseReturnType();
+
+ if (symbol == '^') {
+ exceptionTypes = new ListOfTypes(8);
+ do {
+ scanSymbol();
+
+ // ThrowsSignature ::= ("^" ClassTypeSignature) |
+ // ("^" TypeVariableSignature).
+ if (symbol == 'T') {
+ exceptionTypes.add(parseTypeVariableSignature());
+ } else {
+ exceptionTypes.add(parseClassTypeSignature());
+ }
+ } while (symbol == '^');
+ } else if (rawExceptionTypes != null) {
+ exceptionTypes = new ListOfTypes(rawExceptionTypes);
+ } else {
+ exceptionTypes = new ListOfTypes(0);
+ }
+ }
+
+ Type parseReturnType() {
+ // ReturnType ::= TypeSignature | "V".
+ if (symbol != 'V') { return parseTypeSignature(); }
+ else { scanSymbol(); return void.class; }
+ }
+
+
+ //
+ // Scanner:
+ //
+
+ void scanSymbol() {
+ if (!eof) {
+ if (pos < buffer.length) {
+ symbol = buffer[pos];
+ pos++;
+ } else {
+ symbol = 0;
+ eof = true;
+ }
+ } else {
+ throw new GenericSignatureFormatError();
+ }
+ }
+
+ void expect(char c) {
+ if (symbol == c) {
+ scanSymbol();
+ } else {
+ throw new GenericSignatureFormatError();
+ }
+ }
+
+ boolean isStopSymbol(char ch) {
+ switch (ch) {
+ case ':':
+ case '/':
+ case ';':
+ case '<':
+ case '.':
+ return true;
+ }
+ return false;
+ }
+
+ // PRE: symbol is the first char of the identifier.
+ // POST: symbol = the next symbol AFTER the identifier.
+ void scanIdentifier() {
+ if (!eof) {
+ StringBuilder identBuf = new StringBuilder(32);
+ if (!isStopSymbol(symbol)) {
+ identBuf.append(symbol);
+ do {
+ char ch = buffer[pos];
+ if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A') && (ch <= 'Z')
+ || !isStopSymbol(ch)) {
+ identBuf.append(buffer[pos]);
+ pos++;
+ } else {
+ identifier = identBuf.toString();
+ scanSymbol();
+ return;
+ }
+ } while (pos != buffer.length);
+ identifier = identBuf.toString();
+ symbol = 0;
+ eof = true;
+ } else {
+ // Ident starts with incorrect char.
+ symbol = 0;
+ eof = true;
+ throw new GenericSignatureFormatError();
+ }
+ } else {
+ throw new GenericSignatureFormatError();
+ }
+ }
+
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForArray.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForArray.java
new file mode 100644
index 0000000..c9b76b1
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForArray.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+
+public final class ImplForArray implements GenericArrayType {
+ private final Type componentType;
+
+ public ImplForArray(Type componentType) {
+ this.componentType = componentType;
+ }
+
+ public Type getGenericComponentType() {
+ try {
+ return ((ImplForType)componentType).getResolvedType();
+ } catch (ClassCastException e) {
+ return componentType;
+ }
+ }
+
+ public String toString() {
+ return componentType.toString() + "[]";
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForType.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForType.java
new file mode 100644
index 0000000..b1f05d4
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForType.java
@@ -0,0 +1,88 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public final class ImplForType implements ParameterizedType {
+ private final ListOfTypes args;
+ private final ImplForType ownerType0; // Potentially unresolved.
+ private Type ownerTypeRes;
+ private Class rawType; // Already resolved.
+ private final String rawTypeName;
+ private ClassLoader loader;
+
+
+ public ImplForType(ImplForType ownerType, String rawTypeName,
+ ListOfTypes args, ClassLoader loader) {
+ this.ownerType0 = ownerType;
+ this.rawTypeName = rawTypeName;
+ this.args = args;
+ this.loader = loader;
+ }
+
+
+ public Type[] getActualTypeArguments() {
+ // ASSUMPTION: args is never null!!!
+ return args.getResolvedTypes().clone();
+ }
+
+ public Type getOwnerType() {
+ if (ownerTypeRes == null) {
+ if (ownerType0 != null) {
+ ownerTypeRes = ownerType0.getResolvedType();
+ } else {
+ ownerTypeRes = getRawType().getDeclaringClass();
+ }
+ }
+ return ownerTypeRes;
+ }
+
+ public Class getRawType() {
+ if (rawType == null) {
+ // Here the actual loading of the class has to be performed and the
+ // Exceptions have to be re-thrown TypeNotPresent...
+ // How to deal with member (nested) classes?
+ try {
+ rawType = Class.forName(rawTypeName, false, loader);
+ } catch (ClassNotFoundException e) {
+ throw new TypeNotPresentException(rawTypeName, e);
+ }
+ }
+ return rawType;
+ }
+
+
+ Type getResolvedType() {
+ if (args.getResolvedTypes().length == 0) {
+ return getRawType();
+ } else {
+ return this;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(rawTypeName);
+ if (args.length() > 0) {
+ sb.append("<").append(args).append(">");
+ }
+ return sb.toString();
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForVariable.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForVariable.java
new file mode 100644
index 0000000..783d223
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForVariable.java
@@ -0,0 +1,139 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+
+public final class ImplForVariable<D extends GenericDeclaration> implements TypeVariable<D> {
+ private ImplForVariable<D> formalVar;
+ private final GenericDeclaration declOfVarUser;
+ private final String name;
+ private D genericDeclaration;
+ private ListOfTypes bounds;
+
+ @Override
+ public boolean equals(Object o) {
+ if(!(o instanceof TypeVariable)) {
+ return false;
+ }
+ TypeVariable<?> that = (TypeVariable<?>) o;
+ return getName().equals(that.getName()) &&
+ getGenericDeclaration().equals(that.getGenericDeclaration());
+ }
+
+
+ @Override
+ public int hashCode() {
+ return 31 * getName().hashCode() + getGenericDeclaration().hashCode();
+ }
+
+ /**
+ * @param genericDecl declaration where a type variable is declared
+ * @param name type variable name
+ * @param bounds class and interface bounds
+ */
+ ImplForVariable(D genericDecl, String name, ListOfTypes bounds) {
+ this.genericDeclaration = genericDecl;
+ this.name = name;
+ this.bounds = bounds;
+ this.formalVar = this;
+ this.declOfVarUser = null;
+ }
+
+ /**
+ * @param genericDecl declaration where a type variable is used
+ * @param name type variable name
+ */
+ ImplForVariable(D genericDecl, String name) {
+ this.name = name;
+ this.declOfVarUser = genericDecl;
+ }
+
+ static TypeVariable findFormalVar(GenericDeclaration layer, String name) {
+ TypeVariable[] formalVars = layer.getTypeParameters();
+ for (TypeVariable var : formalVars) {
+ if (name.equals(var.getName())) {
+ return var;
+ }
+ }
+ // resolve() looks up the next level only, if null is returned
+ return null;
+ }
+
+ private static GenericDeclaration nextLayer(GenericDeclaration decl) {
+ if (decl instanceof Class) {
+ // FIXME: Is the following hierarchy correct?:
+ Class cl = (Class)decl;
+ decl = cl.getEnclosingMethod();
+ if (decl != null) {
+ return decl;
+ }
+ decl = cl.getEnclosingConstructor();
+ if (decl != null) {
+ return decl;
+ }
+ return cl.getEnclosingClass();
+ } else if (decl instanceof Method) {
+ return ((Method)decl).getDeclaringClass();
+ } else if (decl instanceof Constructor) {
+ return ((Constructor)decl).getDeclaringClass();
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ void resolve() {
+ if (formalVar != null) {
+ return;
+ }
+ GenericDeclaration curLayer = declOfVarUser;
+ TypeVariable var;
+ while ((var = findFormalVar(curLayer, name)) == null) {
+ curLayer = nextLayer(curLayer);
+ if (curLayer == null) {
+ throw new AssertionError("illegal type variable reference");
+ }
+ }
+ formalVar = (ImplForVariable<D>) var;
+ this.genericDeclaration = formalVar.genericDeclaration;
+ this.bounds = formalVar.bounds;
+ }
+
+ public Type[] getBounds() {
+ resolve();
+ return bounds.getResolvedTypes().clone();
+ }
+
+ public D getGenericDeclaration() {
+ resolve();
+ return genericDeclaration;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForWildcard.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForWildcard.java
new file mode 100644
index 0000000..b0605f2
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ImplForWildcard.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.MalformedParameterizedTypeException;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.Arrays;
+
+public final class ImplForWildcard implements WildcardType {
+
+ private final ListOfTypes extendsBound, superBound;
+
+ public ImplForWildcard(ListOfTypes extendsBound, ListOfTypes superBound) {
+ this.extendsBound = extendsBound;
+ this.superBound = superBound;
+ }
+
+ public Type[] getLowerBounds() throws TypeNotPresentException,
+ MalformedParameterizedTypeException {
+ return superBound.getResolvedTypes().clone();
+ }
+
+ public Type[] getUpperBounds() throws TypeNotPresentException,
+ MalformedParameterizedTypeException {
+ return extendsBound.getResolvedTypes().clone();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(!(o instanceof WildcardType)) {
+ return false;
+ }
+ WildcardType that = (WildcardType) o;
+ return Arrays.equals(getLowerBounds(), that.getLowerBounds()) &&
+ Arrays.equals(getUpperBounds(), that.getUpperBounds());
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * Arrays.hashCode(getLowerBounds()) +
+ Arrays.hashCode(getUpperBounds());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("?");
+ if ((extendsBound.length() == 1 && extendsBound.getResolvedTypes()[0] != Object.class)
+ || extendsBound.length() > 1) {
+ sb.append(" extends ").append(extendsBound);
+ } else if (superBound.length() > 0) {
+ sb.append(" super ").append(superBound);
+ }
+ return sb.toString();
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfTypes.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfTypes.java
new file mode 100644
index 0000000..59470f5
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfTypes.java
@@ -0,0 +1,79 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class ListOfTypes {
+ public static final ListOfTypes EMPTY = new ListOfTypes(0);
+
+ private final ArrayList<Type> types;
+ private Type[] resolvedTypes;
+
+ ListOfTypes(int capacity) {
+ types = new ArrayList<Type>(capacity);
+ }
+
+ ListOfTypes(Type[] types) {
+ this.types = new ArrayList<Type>(types.length);
+ for (Type type : types) {
+ this.types.add(type);
+ }
+ }
+
+ void add(Type type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+ types.add(type);
+ }
+
+ int length() {
+ return types.size();
+ }
+
+ public Type[] getResolvedTypes() {
+ Type[] result = resolvedTypes;
+ return result != null ? result : (resolvedTypes = resolveTypes(types));
+ }
+
+ private Type[] resolveTypes(List<Type> unresolved) {
+ Type[] result = new Type[unresolved.size()];
+ for (int i = 0; i < unresolved.size(); i++) {
+ Type type = unresolved.get(i);
+ try {
+ result[i] = ((ImplForType) type).getResolvedType();
+ } catch (ClassCastException e) {
+ result[i] = type;
+ }
+ }
+ return result;
+ }
+
+ @Override public String toString() {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < types.size(); i++) {
+ if (i > 0) {
+ result.append(", ");
+ }
+ result.append(types.get(i));
+ }
+ return result.toString();
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfVariables.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfVariables.java
new file mode 100644
index 0000000..0e757ac
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/ListOfVariables.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.TypeVariable;
+import java.util.ArrayList;
+
+class ListOfVariables {
+ public static final TypeVariable[] EMPTY = new ImplForVariable[0];
+
+ ArrayList<TypeVariable<?>> array = new ArrayList<TypeVariable<?>>();
+
+ void add (TypeVariable<?> elem) {
+ array.add(elem);
+ }
+
+ TypeVariable<?>[] getArray() {
+ TypeVariable<?>[] a = new TypeVariable[array.size()];
+ return array.toArray(a);
+ }
+}
diff --git a/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/Types.java b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/Types.java
new file mode 100644
index 0000000..bd700fc
--- /dev/null
+++ b/libdvm/src/main/java/org/apache/harmony/luni/lang/reflect/Types.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.apache.harmony.luni.lang.reflect;
+
+import java.lang.reflect.Type;
+
+public class Types {
+
+ public static Type[] getClonedTypeArray(ListOfTypes types) {
+ return types.getResolvedTypes().clone();
+ }
+
+ public static Type getType(Type type) {
+ if (type instanceof ImplForType) {
+ return ((ImplForType)type).getResolvedType();
+ } else {
+ return type;
+ }
+ }
+}
diff --git a/libdvm/src/main/java/sun/misc/Unsafe.java b/libdvm/src/main/java/sun/misc/Unsafe.java
new file mode 100644
index 0000000..884340b
--- /dev/null
+++ b/libdvm/src/main/java/sun/misc/Unsafe.java
@@ -0,0 +1,350 @@
+/*
+ * 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 sun.misc;
+
+import dalvik.system.VMStack;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * The package name notwithstanding, this class is the quasi-standard
+ * way for Java code to gain access to and use functionality which,
+ * when unsupervised, would allow one to break the pointer/type safety
+ * of Java.
+ */
+public final class Unsafe {
+ /** Traditional dalvik name. */
+ private static final Unsafe THE_ONE = new Unsafe();
+ /** Traditional RI name. */
+ private static final Unsafe theUnsafe = THE_ONE;
+
+ /**
+ * This class is only privately instantiable.
+ */
+ private Unsafe() {}
+
+ /**
+ * Gets the unique instance of this class. This is only allowed in
+ * very limited situations.
+ */
+ public static Unsafe getUnsafe() {
+ /*
+ * Only code on the bootclasspath is allowed to get at the
+ * Unsafe instance.
+ */
+ ClassLoader calling = VMStack.getCallingClassLoader();
+ if ((calling != null) && (calling != Unsafe.class.getClassLoader())) {
+ throw new SecurityException("Unsafe access denied");
+ }
+
+ return THE_ONE;
+ }
+
+ /**
+ * Gets the raw byte offset from the start of an object's memory to
+ * the memory used to store the indicated instance field.
+ *
+ * @param field non-null; the field in question, which must be an
+ * instance field
+ * @return the offset to the field
+ */
+ public long objectFieldOffset(Field field) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ throw new IllegalArgumentException(
+ "valid for instance fields only");
+ }
+
+ return objectFieldOffset0(field);
+ }
+
+ /**
+ * Helper for {@link #objectFieldOffset}, which does all the work,
+ * assuming the parameter is deemed valid.
+ *
+ * @param field non-null; the instance field
+ * @return the offset to the field
+ */
+ private static native long objectFieldOffset0(Field field);
+
+ /**
+ * Gets the offset from the start of an array object's memory to
+ * the memory used to store its initial (zeroeth) element.
+ *
+ * @param clazz non-null; class in question; must be an array class
+ * @return the offset to the initial element
+ */
+ public int arrayBaseOffset(Class clazz) {
+ if (! clazz.isArray()) {
+ throw new IllegalArgumentException(
+ "valid for array classes only");
+ }
+
+ return arrayBaseOffset0(clazz);
+ }
+
+ /**
+ * Helper for {@link #arrayBaseOffset}, which does all the work,
+ * assuming the parameter is deemed valid.
+ *
+ * @return the offset to the field
+ */
+ private static native int arrayBaseOffset0(Class clazz);
+
+ /**
+ * Gets the size of each element of the given array class.
+ *
+ * @param clazz non-null; class in question; must be an array class
+ * @return &gt; 0; the size of each element of the array
+ */
+ public int arrayIndexScale(Class clazz) {
+ if (! clazz.isArray()) {
+ throw new IllegalArgumentException(
+ "valid for array classes only");
+ }
+
+ return arrayIndexScale0(clazz);
+ }
+
+ /**
+ * Helper for {@link #arrayIndexScale}, which does all the work,
+ * assuming the parameter is deemed valid.
+ *
+ * @return the offset to the field
+ */
+ private static native int arrayIndexScale0(Class clazz);
+
+ /**
+ * Performs a compare-and-set operation on an <code>int</code>
+ * field within the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param expectedValue expected value of the field
+ * @param newValue new value to store in the field if the contents are
+ * as expected
+ * @return <code>true</code> if the new value was in fact stored, and
+ * <code>false</code> if not
+ */
+ public native boolean compareAndSwapInt(Object obj, long offset,
+ int expectedValue, int newValue);
+
+ /**
+ * Performs a compare-and-set operation on a <code>long</code>
+ * field within the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param expectedValue expected value of the field
+ * @param newValue new value to store in the field if the contents are
+ * as expected
+ * @return <code>true</code> if the new value was in fact stored, and
+ * <code>false</code> if not
+ */
+ public native boolean compareAndSwapLong(Object obj, long offset,
+ long expectedValue, long newValue);
+
+ /**
+ * Performs a compare-and-set operation on an <code>Object</code>
+ * field (that is, a reference field) within the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param expectedValue expected value of the field
+ * @param newValue new value to store in the field if the contents are
+ * as expected
+ * @return <code>true</code> if the new value was in fact stored, and
+ * <code>false</code> if not
+ */
+ public native boolean compareAndSwapObject(Object obj, long offset,
+ Object expectedValue, Object newValue);
+
+ /**
+ * Gets an <code>int</code> field from the given object,
+ * using <code>volatile</code> semantics.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @return the retrieved value
+ */
+ public native int getIntVolatile(Object obj, long offset);
+
+ /**
+ * Stores an <code>int</code> field into the given object,
+ * using <code>volatile</code> semantics.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param newValue the value to store
+ */
+ public native void putIntVolatile(Object obj, long offset, int newValue);
+
+ /**
+ * Gets a <code>long</code> field from the given object,
+ * using <code>volatile</code> semantics.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @return the retrieved value
+ */
+ public native long getLongVolatile(Object obj, long offset);
+
+ /**
+ * Stores a <code>long</code> field into the given object,
+ * using <code>volatile</code> semantics.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param newValue the value to store
+ */
+ public native void putLongVolatile(Object obj, long offset, long newValue);
+
+ /**
+ * Gets an <code>Object</code> field from the given object,
+ * using <code>volatile</code> semantics.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @return the retrieved value
+ */
+ public native Object getObjectVolatile(Object obj, long offset);
+
+ /**
+ * Stores an <code>Object</code> field into the given object,
+ * using <code>volatile</code> semantics.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param newValue the value to store
+ */
+ public native void putObjectVolatile(Object obj, long offset,
+ Object newValue);
+
+ /**
+ * Gets an <code>int</code> field from the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @return the retrieved value
+ */
+ public native int getInt(Object obj, long offset);
+
+ /**
+ * Stores an <code>int</code> field into the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param newValue the value to store
+ */
+ public native void putInt(Object obj, long offset, int newValue);
+
+ /**
+ * Lazy set an int field.
+ */
+ public native void putOrderedInt(Object obj, long offset, int newValue);
+
+ /**
+ * Gets a <code>long</code> field from the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @return the retrieved value
+ */
+ public native long getLong(Object obj, long offset);
+
+ /**
+ * Stores a <code>long</code> field into the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param newValue the value to store
+ */
+ public native void putLong(Object obj, long offset, long newValue);
+
+ /**
+ * Lazy set a long field.
+ */
+ public native void putOrderedLong(Object obj, long offset, long newValue);
+
+ /**
+ * Gets an <code>Object</code> field from the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @return the retrieved value
+ */
+ public native Object getObject(Object obj, long offset);
+
+ /**
+ * Stores an <code>Object</code> field into the given object.
+ *
+ * @param obj non-null; object containing the field
+ * @param offset offset to the field within <code>obj</code>
+ * @param newValue the value to store
+ */
+ public native void putObject(Object obj, long offset, Object newValue);
+
+ /**
+ * Lazy set an object field.
+ */
+ public native void putOrderedObject(Object obj, long offset,
+ Object newValue);
+
+ /**
+ * Parks the calling thread for the specified amount of time,
+ * unless the "permit" for the thread is already available (due to
+ * a previous call to {@link #unpark}. This method may also return
+ * spuriously (that is, without the thread being told to unpark
+ * and without the indicated amount of time elapsing).
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * @param absolute whether the given time value is absolute
+ * milliseconds-since-the-epoch (<code>true</code>) or relative
+ * nanoseconds-from-now (<code>false</code>)
+ * @param time the (absolute millis or relative nanos) time value
+ */
+ public void park(boolean absolute, long time) {
+ if (absolute) {
+ Thread.currentThread().parkUntil(time);
+ } else {
+ Thread.currentThread().parkFor(time);
+ }
+ }
+
+ /**
+ * Unparks the given object, which must be a {@link Thread}.
+ *
+ * <p>See {@link java.util.concurrent.locks.LockSupport} for more
+ * in-depth information of the behavior of this method.</p>
+ *
+ * @param obj non-null; the object to unpark
+ */
+ public void unpark(Object obj) {
+ if (obj instanceof Thread) {
+ ((Thread) obj).unpark();
+ } else {
+ throw new IllegalArgumentException("valid for Threads only");
+ }
+ }
+
+ /**
+ * Allocates an instance of the given class without running the constructor.
+ * The class' <clinit> will be run, if necessary.
+ */
+ public native Object allocateInstance(Class<?> c);
+}