diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
commit | adc854b798c1cfe3bfd4c27d68d5cee38ca617da (patch) | |
tree | 6aed8b4923ca428942cbaa7e848d50237a3d31e0 /luni-kernel/src | |
parent | 1c0fed63c71ddb230f3b304aac12caffbedf2f21 (diff) | |
download | libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.zip libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.gz libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'luni-kernel/src')
34 files changed, 15454 insertions, 0 deletions
diff --git a/luni-kernel/src/main/java/java/lang/Class.java b/luni-kernel/src/main/java/java/lang/Class.java new file mode 100644 index 0000000..70ae3c1 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/Class.java @@ -0,0 +1,1627 @@ +/* + * 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 org.apache.harmony.kernel.vm.StringUtils; +import org.apache.harmony.luni.lang.reflect.GenericSignatureParser; +import org.apache.harmony.luni.lang.reflect.Types; + +import java.io.InputStream; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +import java.lang.ref.SoftReference; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Collection; +import java.util.HashMap; +import java.net.URL; +import java.security.ProtectionDomain; + +import static java.lang.ClassCache.REFLECT; +import static java.lang.ClassCache.compareClassLists; +import static java.lang.ClassCache.findMethodByName; +import static java.lang.ClassCache.findFieldByName; + +/** + * 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> + * + * @since Android 1.0 + */ +public final class Class<T> implements Serializable, AnnotatedElement, GenericDeclaration, Type { + + private static final long serialVersionUID = 3206093459760846163L; + + // TODO How is this field being initialized? What's it being used for? + private ProtectionDomain pd; + + /** + * null-ok; cache of reflective information, wrapped in a soft + * reference + */ + private volatile SoftReference<ClassCache<T>> cacheRef; + + 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 + * specified name. The name should be the name of a class as described in + * the {@link Class class definition}; however, {@code Class}es representing + * primitive types can not be found using this method. + * <p> + * If the class has not been loaded so far, it is being loaded and linked + * first. This is done through either the class loader of the calling class + * or one of its parent class loaders. The class is also being initialized, + * which means that a possible static initializer block is executed. + * + * @param className + * the name of the non-primitive-type class to find. + * @return the named {@code Class} instance. + * @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. + * @since Android 1.0 + */ + 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 + * specified name. The name should be the name of a class as described in + * the {@link Class class definition}, however {@code Class}es representing + * primitive types can not be found using this method. Security rules will + * be obeyed. + * <p> + * If the class has not been loaded so far, it is being loaded and linked + * first. This is done through either the specified class loader or one of + * its parent class loaders. The caller can also request the class to be + * initialized, which means that a possible static initializer block is + * executed. + * + * @param className + * the name of the non-primitive-type class to find. + * @param initializeBoolean + * indicates whether the class should be initialized. + * @param classLoader + * the class loader to use to load the class. + * @return the named {@code Class} instance. + * @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. + * @since Android 1.0 + */ + public static Class<?> forName(String className, boolean initializeBoolean, + ClassLoader classLoader) throws ClassNotFoundException { + + if (classLoader == null) { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + ClassLoader calling = VMStack.getCallingClassLoader(); + if (calling != null) { + smgr.checkPermission(new RuntimePermission("getClassLoader")); + } + } + + 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 ExceptionInInitilaizerError 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, initializeBoolean, + classLoader); + } catch (ClassNotFoundException e) { + Throwable cause = e.getCause(); + if (cause instanceof ExceptionInInitializerError) { + throw (ExceptionInInitializerError) cause; + } + throw e; + } + return result; + } + + /* + * Returns a class by name without any security checks. + * + * @param className The name of the non-primitive type class to find + * @param initializeBoolean A boolean indicating whether the class should be + * initialized + * @param classLoader The class loader to use to load the class + * @return the named class. + * @throws ClassNotFoundException If the class could not be found + */ + static native Class<?> classForName(String className, boolean initializeBoolean, + 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. + * + * @return the public class members of the class represented by this object. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @since Android 1.0 + */ + public Class[] getClasses() { + // BEGIN android-note + // trying to get closer to the RI which returns a raw class array. + // copied from newer version of harmony + // END android-note + checkPublicMemberAccess(); + return getFullListOfClasses(true); + } + + /** + * Returns the annotation of the given type. If there is no such annotation + * then the method returns {@code null}. + * + * @param annotationClass + * the annotation type. + * @return the annotation of the given type, or {@code null} if there is no + * such annotation. + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public <A extends Annotation> A getAnnotation(Class<A> annotationClass) { + Annotation[] list = getAnnotations(); + for (int i = 0; i < list.length; i++) { + if (annotationClass.isInstance(list[i])) { + return (A)list[i]; + } + } + + return null; + } + + /** + * Returns all the annotations of this class. If there are no annotations + * then an empty array is returned. + * + * @return a copy of the array containing this class' annotations. + * @see #getDeclaredAnnotations() + * @since Android 1.0 + */ + 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[] annos = getDeclaredAnnotations(); + + for (int i = annos.length-1; i >= 0; --i) + map.put(annos[i].annotationType(), annos[i]); + + for (Class sup = getSuperclass(); sup != null; + sup = sup.getSuperclass()) { + annos = sup.getDeclaredAnnotations(); + for (int i = annos.length-1; i >= 0; --i) { + Class clazz = annos[i].annotationType(); + if (!map.containsKey(clazz) && + clazz.isAnnotationPresent(Inherited.class)) { + map.put(clazz, annos[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}. + * + * @return this class' canonical name, or {@code null} if it does not have a + * canonical name. + * @since Android 1.0 + */ + 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, returns a reference to an actual + * representation of the bootstrap class loader. + * + * @return the class loader for the represented class. + * @throws SecurityException + * if a security manager exists and it does not allow accessing + * the class loader. + * @see ClassLoader + * @since Android 1.0 + */ + public ClassLoader getClassLoader() { + SecurityManager smgr = System.getSecurityManager(); + ClassLoader loader = getClassLoaderImpl(); + if (smgr != null && loader != null) { + ClassLoader calling = VMStack.getCallingClassLoader(); + + if (calling != null && !calling.isAncestorOf(loader)) { + smgr.checkPermission(new RuntimePermission("getClassLoader")); + } + } + + if (this.isPrimitive()) { + return null; + } + + 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.checkMemberAccess(), 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. + * + * @return the ClassLoader + * @see ClassLoader#isSystemClassLoader() + */ + ClassLoader getClassLoaderImpl() { + return getClassLoader(this); + } + + /* + * Returns the defining class loader for the given class. + * + * @param clazz the class the class loader of which we want + * @return the class loader + */ + private static native ClassLoader getClassLoader(Class<?> clazz); + + /** + * 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. + * + * @return the component type of this class. + * @since Android 1.0 + */ + public native Class<?> getComponentType(); + + /** + * Returns a {@code Constructor} object which represents the public + * constructor matching the specified parameter types. + * + * @param parameterTypes + * the parameter types of the requested constructor. + * @return the constructor described by {@code parameterTypes}. + * @throws NoSuchMethodException + * if the constructor can not be found. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getDeclaredConstructor(Class...) + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public Constructor<T> getConstructor(Class... parameterTypes) throws NoSuchMethodException, + SecurityException { + checkPublicMemberAccess(); + return getMatchingConstructor(getDeclaredConstructors(this, true), parameterTypes); + } + + /** + * Returns an array containing {@code Constructor} objects for all public + * constructors for the class represented by 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. + * + * @return an array with the public constructors of the class represented by + * this {@code Class}. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getDeclaredConstructors() + * @since Android 1.0 + */ + public Constructor[] getConstructors() throws SecurityException { + // BEGIN android-note + // trying to get closer to the RI which returns a raw constructor array. + // copied from newer version of harmony + // END android-note + checkPublicMemberAccess(); + 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. + * + * @return a copy of the array containing the annotations defined for the + * class that this {@code Class} represents. + * @see #getAnnotations() + * @since Android 1.0 + */ + native public Annotation[] getDeclaredAnnotations(); + + /** + * 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. + * + * @return an array with {@code Class} objects for all the classes and + * interfaces that are used in member declarations. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @since Android 1.0 + */ + public Class[] getDeclaredClasses() throws SecurityException { + // BEGIN android-note + // trying to get closer to the RI which returns a raw class array. + // copied from newer version of harmony + // END android-note + checkDeclaredMemberAccess(); + return getDeclaredClasses(this, false); + } + + /* + * Returns the list of member classes without performing any security checks + * first. This includes the member classes inherited from superclasses. If no + * member classes exist at all, an empty array is returned. + * + * @param publicOnly reflects whether we want only public members or all of them + * @return the list of classes + */ + private Class<?>[] getFullListOfClasses(boolean publicOnly) { + Class<?>[] result = getDeclaredClasses(this, publicOnly); + + // Traverse all superclasses + Class<?> clazz = this.getSuperclass(); + while (clazz != null) { + Class<?>[] temp = getDeclaredClasses(clazz, publicOnly); + if (temp.length != 0) { + result = arraycopy(new Class[result.length + temp.length], result, temp); + } + + clazz = clazz.getSuperclass(); + } + + return result; + } + + /* + * Returns the list of member classes of the given class. No security checks + * are performed. If no members exist, an empty array is returned. + * + * @param clazz the class the members of which we want + * @param publicOnly reflects whether we want only public member or all of them + * @return the class' class members + */ + native private static Class<?>[] getDeclaredClasses(Class<?> clazz, + boolean publicOnly); + + /** + * Returns a {@code Constructor} object which represents the constructor + * matching the specified parameter types that is declared by the class + * represented by this {@code Class}. + * + * @param parameterTypes + * the parameter types of the requested constructor. + * @return the constructor described by {@code parameterTypes}. + * @throws NoSuchMethodException + * if the requested constructor can not be found. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getConstructor(Class...) + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public Constructor<T> getDeclaredConstructor(Class... parameterTypes) + throws NoSuchMethodException, SecurityException { + checkDeclaredMemberAccess(); + return getMatchingConstructor(getDeclaredConstructors(this, 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. + * + * @return an array with the constructors declared in the class represented + * by this {@code Class}. + * + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getConstructors() + * @since Android 1.0 + */ + public Constructor[] getDeclaredConstructors() throws SecurityException { + // BEGIN android-note + // trying to get closer to the RI which returns a raw constructor array. + // copied from newer version of harmony + // END android-note + checkDeclaredMemberAccess(); + return getDeclaredConstructors(this, false); + } + + /* + * Returns the list of constructors without performing any security checks + * first. If no constructors exist, an empty array is returned. + * + * @param clazz the class of interest + * @param publicOnly reflects whether we want only public constructors or all of them + * @return the list of constructors + */ + private static native <T> Constructor<T>[] getDeclaredConstructors(Class<T> clazz, boolean publicOnly); + + /* + * Finds a constructor with a given signature. + * + * @param list the list of constructors to search through + * @param parameterTypes the formal parameter list + * @return the matching constructor + * @throws NoSuchMethodException if the constructor does not exist. + */ + private Constructor<T> getMatchingConstructor( + Constructor<T>[] list, Class<?>[] parameterTypes) + throws NoSuchMethodException { + for (int i = 0; i < list.length; i++) { + if (compareClassLists(list[i].getParameterTypes(), parameterTypes)) { + return list[i]; + } + } + + // BEGIN android-changed + StringBuilder sb = new StringBuilder(); + sb.append(getSimpleName()); + sb.append('('); + boolean first = true; + for (Class<?> p : parameterTypes) { + if (!first) { + sb.append(','); + } + first = false; + sb.append(p.getSimpleName()); + } + sb.append(')'); + throw new NoSuchMethodException(sb.toString()); + // END android-changed + } + + /** + * Returns a {@code Field} object for the field with the specified name + * which is declared in the class represented by this {@code Class}. + * + * @param name + * the name of the requested field. + * @return the requested field in the class represented by this class. + * @throws NoSuchFieldException + * if the requested field can not be found. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getField(String) + * @since Android 1.0 + */ + public Field getDeclaredField(String name) + throws NoSuchFieldException, SecurityException { + checkDeclaredMemberAccess(); + + Field[] fields = getClassCache().getDeclaredFields(); + Field field = findFieldByName(fields, name); + + /* + * Make a copy of the private (to the package) object, so that + * setAccessible() won't alter the private instance. + */ + return REFLECT.clone(field); + } + + /** + * 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. + * + * @return an array with the fields declared in the class represented by + * this class. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getFields() + * @since Android 1.0 + */ + public Field[] getDeclaredFields() throws SecurityException { + checkDeclaredMemberAccess(); + + // Return a copy of the private (to the package) array. + Field[] fields = getClassCache().getDeclaredFields(); + return ClassCache.deepCopy(fields); + } + + /* + * Returns the list of fields without performing any security checks + * first. If no fields exist at all, an empty array is returned. + * + * @param clazz the class of interest + * @param publicOnly reflects whether we want only public fields or all of them + * @return the list of fields + */ + static native Field[] getDeclaredFields(Class<?> clazz, boolean publicOnly); + + /** + * Returns a {@code Method} object which represents the method matching the + * specified name and parameter types that is declared by the class + * represented by this {@code Class}. + * + * @param name + * the requested method's name. + * @param parameterTypes + * the parameter types of the requested method. + * @return the method described by {@code name} and {@code parameterTypes}. + * @throws NoSuchMethodException + * if the requested constructor can not be found. + * @throws NullPointerException + * if {@code name} is {@code null}. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getMethod(String, Class...) + * @since Android 1.0 + */ + public Method getDeclaredMethod(String name, Class... parameterTypes) + throws NoSuchMethodException, SecurityException { + checkDeclaredMemberAccess(); + + Method[] methods = getClassCache().getDeclaredMethods(); + Method method = findMethodByName(methods, name, parameterTypes); + + /* + * Make a copy of the private (to the package) object, so that + * setAccessible() won't alter the private instance. + */ + return REFLECT.clone(method); + } + + /** + * 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. + * + * @return an array with the methods declared in the class represented by + * this {@code Class}. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getMethods() + * @since Android 1.0 + */ + public Method[] getDeclaredMethods() throws SecurityException { + checkDeclaredMemberAccess(); + + // Return a copy of the private (to the package) array. + Method[] methods = getClassCache().getDeclaredMethods(); + return ClassCache.deepCopy(methods); + } + + /** + * Returns the list of methods without performing any security checks + * first. If no methods exist, an empty array is returned. + */ + static native Method[] getDeclaredMethods(Class<?> clazz, boolean publicOnly); + + /** + * Gets the {@link ClassCache} for this instance. + * + * @return non-null; the cache object + */ + /*package*/ ClassCache<T> getClassCache() { + /* + * Note: It is innocuous if two threads try to simultaneously + * create the cache, so we don't bother protecting against that. + */ + ClassCache<T> cache = null; + + if (cacheRef != null) { + cache = cacheRef.get(); + } + + if (cache == null) { + cache = new ClassCache<T>(this); + cacheRef = new SoftReference<ClassCache<T>>(cache); + } + + return cache; + } + + /** + * 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. + * + * @return the declaring {@code Class} or {@code null}. + * @since Android 1.0 + */ + native public Class<?> getDeclaringClass(); + + /** + * Returns the enclosing {@code Class} of this {@code Class}. If there is no + * enclosing class the method returns {@code null}. + * + * @return the enclosing {@code Class} or {@code null}. + * @since Android 1.0 + */ + native public Class<?> getEnclosingClass(); + + /** + * Gets the enclosing {@code Constructor} of this {@code Class}, if it is an + * anonymous or local/automatic class; otherwise {@code null}. + * + * @return the enclosing {@code Constructor} instance or {@code null}. + * @since Android 1.0 + */ + native public Constructor<?> getEnclosingConstructor(); + + /** + * Gets the enclosing {@code Method} of this {@code Class}, if it is an + * anonymous or local/automatic class; otherwise {@code null}. + * + * @return the enclosing {@code Method} instance or {@code null}. + * @since Android 1.0 + */ + native public Method getEnclosingMethod(); + + /** + * Gets the {@code enum} constants associated with this {@code Class}. + * Returns {@code null} if this {@code Class} does not represent an {@code + * enum} type. + * + * @return an array with the {@code enum} constants or {@code null}. + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public T[] getEnumConstants() { + if (isEnum()) { + checkPublicMemberAccess(); + T[] values = getClassCache().getEnumValuesInOrder(); + + // Copy the private (to the package) array. + return (T[]) values.clone(); + } + + return null; + } + + /** + * Returns a {@code Field} object which represents the public field with the + * specified 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. + * + * @param name + * the name of the requested field. + * @return the public field specified by {@code name}. + * @throws NoSuchFieldException + * if the field can not be found. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getDeclaredField(String) + * @since Android 1.0 + */ + public Field getField(String name) throws NoSuchFieldException, SecurityException { + checkPublicMemberAccess(); + + Field[] fields = getClassCache().getAllPublicFields(); + Field field = findFieldByName(fields, name); + + /* + * Make a copy of the private (to the package) object, so that + * setAccessible() won't alter the private instance. + */ + return REFLECT.clone(field); + } + + /** + * 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. + * </p> + * + * @return an array with the public fields of the class represented by this + * {@code Class}. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getDeclaredFields() + * @since Android 1.0 + */ + public Field[] getFields() throws SecurityException { + checkPublicMemberAccess(); + + // Return a copy of the private (to the package) array. + Field[] fields = getClassCache().getAllPublicFields(); + return ClassCache.deepCopy(fields); + } + + /** + * Gets 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. + * + * @return an array of {@link Type} instances directly implemented by the + * class represented by this {@code class}. + * @since Android 1.0 + */ + public Type[] getGenericInterfaces() { + GenericSignatureParser parser = new GenericSignatureParser( + VMStack.getCallingClassLoader2()); + parser.parseForClass(this, getSignatureAttribute()); + return Types.getClonedTypeArray(parser.interfaceTypes); + } + + /** + * Gets the {@code Type} that represents the superclass of this {@code + * class}. + * + * @return an instance of {@code Type} representing the superclass. + * @since Android 1.0 + */ + public Type getGenericSuperclass() { + GenericSignatureParser parser = new GenericSignatureParser( + VMStack.getCallingClassLoader2()); + parser.parseForClass(this, getSignatureAttribute()); + return Types.getType(parser.superclassType); + } + + /** + * Returns an array of {@code Class} objects that match the interfaces + * specified 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. + * + * @return an array with the interfaces of the class represented by this + * class. + * @since Android 1.0 + */ + public native Class[] getInterfaces(); + // BEGIN android-note + // trying to get closer to the RI which returns a raw class array. + // copied from newer version of harmony + // END android-note + + // Changed to raw type to be closer to the RI + /** + * Returns a {@code Method} object which represents the public method with + * the specified name and parameter types. 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. + * + * @param name + * the requested method's name. + * @param parameterTypes + * the parameter types of the requested method. + * @return the public field specified by {@code name}. + * @throws NoSuchMethodException + * if the method can not be found. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getDeclaredMethod(String, Class...) + * @since Android 1.0 + */ + public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException, + SecurityException { + checkPublicMemberAccess(); + + Method[] methods = getClassCache().getAllPublicMethods(); + Method method = findMethodByName(methods, name, parameterTypes); + + /* + * Make a copy of the private (to the package) object, so that + * setAccessible() won't alter the private instance. + */ + return REFLECT.clone(method); + } + + /** + * 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. + * </p> + * + * @return an array with the methods of the class represented by this + * {@code Class}. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + * @see #getDeclaredMethods() + * @since Android 1.0 + */ + public Method[] getMethods() throws SecurityException { + checkPublicMemberAccess(); + + // Return a copy of the private (to the package) array. + Method[] methods = getClassCache().getAllPublicMethods(); + return ClassCache.deepCopy(methods); + } + + /** + * Performs the security checks regarding the access of a public + * member of this {@code Class}. + * + * <p><b>Note:</b> Because of the {@code getCallingClassLoader2()} + * check, this method must be called exactly one level deep into a + * public method on this instance.</p> + */ + /*package*/ void checkPublicMemberAccess() { + SecurityManager smgr = System.getSecurityManager(); + + if (smgr != null) { + smgr.checkMemberAccess(this, Member.PUBLIC); + + ClassLoader calling = VMStack.getCallingClassLoader2(); + ClassLoader current = getClassLoader(); + + if (calling != null && !calling.isAncestorOf(current)) { + smgr.checkPackageAccess(this.getPackage().getName()); + } + } + } + + /** + * Performs the security checks regarding the access of a declared + * member of this {@code Class}. + * + * <p><b>Note:</b> Because of the {@code getCallingClassLoader2()} + * check, this method must be called exactly one level deep into a + * public method on this instance.</p> + */ + private void checkDeclaredMemberAccess() { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkMemberAccess(this, Member.DECLARED); + + ClassLoader calling = VMStack.getCallingClassLoader2(); + ClassLoader current = getClassLoader(); + + if (calling != null && !calling.isAncestorOf(current)) { + smgr.checkPackageAccess(this.getPackage().getName()); + } + } + } + + /** + * 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. + * + * @return the modifiers of the class represented by this {@code Class}. + * @since Android 1.0 + */ + public int getModifiers() { + return getModifiers(this, false); + } + + /* + * Return the modifiers for the given class. + * + * @param clazz the class of interest + * @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}. + * + * @return the name of the class represented by this {@code Class}. + * @since Android 1.0 + */ + public native String getName(); + + /** + * 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}. + * @since Android 1.0 + */ + 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. + * + * @return The name. + */ + private native String getInnerClassName(); + + /** + * Returns the {@code ProtectionDomain} of the class represented by this + * class. + * <p> + * Note: In order to conserve space in an embedded target like Android, we + * allow this method to return {@code null} for classes in the system + * protection domain (that is, for system classes). System classes are + * always given full permissions (that is, AllPermission). This can not be + * changed through the {@link java.security.Policy} class. + * </p> + * + * @return the {@code ProtectionDomain} of the class represented by this + * class. + * @throws SecurityException + * if a security manager exists and it does not allow member + * access. + */ + public ProtectionDomain getProtectionDomain() { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + // Security check is independent of calling class loader. + smgr.checkPermission(new RuntimePermission("getProtectionDomain")); + } + + return pd; + } + + /** + * Returns the URL of the resource specified by {@code resName}. The mapping + * between the resource name and the URL is managed by the class' class + * loader. + * + * @param resName + * the name of the resource. + * @return the requested resource's {@code URL} object or {@code null} if + * the resource can not be found. + * @see ClassLoader + * @since Android 1.0 + */ + public URL getResource(String resName) { + // Get absolute resource name, but without the leading slash + if (resName.startsWith("/")) { + resName = resName.substring(1); + } else { + String pkg = getName(); + int dot = pkg.lastIndexOf('.'); + if (dot != -1) { + pkg = pkg.substring(0, dot).replace('.', '/'); + } else { + pkg = ""; + } + + resName = pkg + "/" + resName; + } + + // Delegate to proper class loader + ClassLoader loader = getClassLoader(); + if (loader != null) { + return loader.getResource(resName); + } else { + return ClassLoader.getSystemResource(resName); + } + } + + /** + * Returns a read-only stream for the contents of the resource specified by + * {@code resName}. The mapping between the resource name and the stream is + * managed by the class' class loader. + * + * @param resName + * the name of the resource. + * @return a stream for the requested resource or {@code null} if no + * resource with the specified name can be found. + * @see ClassLoader + * @since Android 1.0 + */ + public InputStream getResourceAsStream(String resName) { + // Get absolute resource name, but without the leading slash + if (resName.startsWith("/")) { + resName = resName.substring(1); + } else { + String pkg = getName(); + int dot = pkg.lastIndexOf('.'); + if (dot != -1) { + pkg = pkg.substring(0, dot).replace('.', '/'); + } else { + pkg = ""; + } + + resName = pkg + "/" + resName; + } + + // Delegate to proper class loader + ClassLoader loader = getClassLoader(); + if (loader != null) { + return loader.getResourceAsStream(resName); + } else { + return ClassLoader.getSystemResourceAsStream(resName); + } + } + + /** + * Returns the signers for the class represented by this {@code Class} or + * {@code null} if either there are no signers or this {@code Class} + * represents a primitive type or void. + * + * @return the signers of the class represented by this {@code Class}. + * @since Android 1.0 + */ + public Object[] getSigners() { + // TODO Delegate this to class loader somehow? What are these signers? + 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. + * + * @return the superclass of the class represented by this {@code Class}. + * @since Android 1.0 + */ + 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. + * + * @return an array with the type variables of the class represented by this + * class. + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public synchronized TypeVariable<Class<T>>[] getTypeParameters() { + GenericSignatureParser parser = new GenericSignatureParser( + VMStack.getCallingClassLoader2()); + parser.parseForClass(this, getSignatureAttribute()); + return parser.formalTypeParameters.clone(); + } + + /** + * Indicates whether this {@code Class} represents an annotation class. + * + * @return {@code true} if this {@code Class} represents an annotation + * class; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isAnnotation() { + final int ACC_ANNOTATION = 0x2000; // not public in reflect.Modifiers + int mod = getModifiers(this, true); + return (mod & ACC_ANNOTATION) != 0; + } + + /** + * Indicates whether the specified annotation is present for the class + * represented by this {@code Class}. + * + * @param annotationClass + * the annotation to look for. + * @return {@code true} if the class represented by this {@code Class} is + * annotated with {@code annotationClass}; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { + return getAnnotation(annotationClass) != null; + } + + /** + * Indicates whether the class represented by this {@code Class} is + * anonymously declared. + * + * @return {@code true} if the class represented by this {@code Class} is + * anonymous; {@code false} otherwise. + * @since Android 1.0 + */ + native public boolean isAnonymousClass(); + + /** + * Indicates whether the class represented by this {@code Class} is an array + * class. + * + * @return {@code true} if the class represented by this {@code Class} is an + * array class; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isArray() { + return getComponentType() != null; + } + + /** + * Indicates whether the specified 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). + * + * @param cls + * the class to check. + * @return {@code true} if {@code cls} can be converted to the class + * represented by this {@code Class}; {@code false} otherwise. + * @throws NullPointerException + * if {@code cls} is {@code null}. + * @since Android 1.0 + */ + public native boolean isAssignableFrom(Class<?> cls); + + /** + * Indicates whether the class represented by this {@code Class} is an + * {@code enum}. + * + * @return {@code true} if the class represented by this {@code Class} is an + * {@code enum}; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isEnum() { + return ((getModifiers() & 0x4000) != 0) && (getSuperclass() == Enum.class); + } + + /** + * Indicates whether the specified object can be cast to the class + * represented by this {@code Class}. This is the runtime version of the + * {@code instanceof} operator. + * + * @param object + * the object to check. + * @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. + * @since Android 1.0 + */ + public native boolean isInstance(Object object); + + /** + * Indicates whether this {@code Class} represents an interface. + * + * @return {@code true} if this {@code Class} represents an interface; + * {@code false} otherwise. + * @since Android 1.0 + */ + public native boolean isInterface(); + + /** + * Indicates whether the class represented by this {@code Class} is defined + * locally. + * + * @return {@code true} if the class represented by this {@code Class} is + * defined locally; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isLocalClass() { + boolean enclosed = (getEnclosingMethod() != null || + getEnclosingConstructor() != null); + return enclosed && !isAnonymousClass(); + } + + /** + * Indicates whether the class represented by this {@code Class} is a member + * class. + * + * @return {@code true} if the class represented by this {@code Class} is a + * member class; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isMemberClass() { + return getDeclaringClass() != null; + } + + /** + * Indicates whether this {@code Class} represents a primitive type. + * + * @return {@code true} if this {@code Class} represents a primitive type; + * {@code false} otherwise. + * @since Android 1.0 + */ + public native boolean isPrimitive(); + + /** + * Indicates whether this {@code Class} represents a synthetic type. + * + * @return {@code true} if this {@code Class} represents a synthetic type; + * {@code false} otherwise. + * @since Android 1.0 + */ + 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. + * + * @return a new instance of the class represented by this {@code Class}. + * @throws IllegalAccessException + * if the default constructor is not visible. + * @throws InstantiationException + * if the instance can not be created. + * @throws SecurityException + * if a security manager exists and it does not allow creating + * new instances. + * @since Android 1.0 + */ + public T newInstance() throws IllegalAccessException, + InstantiationException { + checkPublicMemberAccess(); + return newInstanceImpl(); + } + + private native T newInstanceImpl() throws IllegalAccessException, + InstantiationException; + + @Override + public String toString() { + if (isPrimitive()) { + return getSimpleName().toLowerCase(); + } 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. + * + * @return Package the {@code Package} of which this {@code Class} is a + * member or {@code null}. + * @since Android 1.0 + */ + 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 ? ClassLoader.getPackage(loader, 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. + * + * @return the assertion status for the class represented by this {@code + * Class}. + * @since Android 1.0 + */ + public native boolean desiredAssertionStatus(); + + /** + * Casts this {@code Class} to represent a subclass of the specified class. + * If successful, this {@code Class} is returned; otherwise a {@code + * ClassCastException} is thrown. + * + * @param clazz + * the required type. + * @return this {@code Class} cast as a subclass of the given type. + * @throws ClassCastException + * if this {@code Class} cannot be cast to the specified type. + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public <U> Class<? extends U> asSubclass(Class<U> clazz) { + if (clazz.isAssignableFrom(this)) { + return (Class<? extends U>)this; + } + throw new ClassCastException(); + } + + /** + * Casts the specified object to the type represented by this {@code Class}. + * If the object is {@code null} then the result is also {@code null}. + * + * @param obj + * the object to cast. + * @return the object that has been cast. + * @throws ClassCastException + * if the object cannot be cast to the specified type. + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public T cast(Object obj) { + if (obj == null) { + return null; + } else if (this.isInstance(obj)) { + return (T)obj; + } + throw new ClassCastException(); + } + + /** + * Set the "accessible" flag of the given object, without doing any + * access checks. + * + * <p><b>Note:</b> This method is implemented in native code, and, + * as such, is less efficient than using {@link ClassCache#REFLECT} + * to achieve the same goal. This method exists solely to help + * bootstrap the reflection bridge.</p> + * + * @param ao non-null; the object to modify + * @param flag the new value for the accessible flag + */ + /*package*/ static native void setAccessibleNoCheck(AccessibleObject ao, + boolean flag); + + /** + * 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; + } + + /** + * This must be provided by the vm vendor, as it is used by other provided + * class implementations in this package. This method is used by + * SecurityManager.classDepth(), and getClassContext() which use the + * parameters (-1, false) and SecurityManager.classLoaderDepth(), + * currentClassLoader(), and currentLoadedClass() which use the parameters + * (-1, true). Walk the stack and answer an array containing the maxDepth + * most recent classes on the stack of the calling thread. Starting with the + * caller of the caller of getStackClasses(), return an array of not more + * than maxDepth Classes representing the classes of running methods on the + * stack (including native methods). Frames representing the VM + * implementation of java.lang.reflect are not included in the list. If + * stopAtPrivileged is true, the walk will terminate at any frame running + * one of the following methods: <code><ul> + * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;)Ljava/lang/Object;</li> + * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;</li> + * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;</li> + * <li>java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;</li> + * </ul></code> If one of the doPrivileged methods is found, the walk terminate + * and that frame is NOT included in the returned array. Notes: + * <ul> + * <li>This method operates on the defining classes of methods on stack. + * NOT the classes of receivers.</li> + * <li>The item at index zero in the result array describes the caller of + * the caller of this method.</li> + * </ul> + * + * @param maxDepth + * maximum depth to walk the stack, -1 for the entire stack + * @param stopAtPrivileged + * stop at privileged classes + * @return the array of the most recent classes on the stack + */ + static final Class<?>[] getStackClasses(int maxDepth, boolean stopAtPrivileged) { + return VMStack.getClasses(maxDepth, stopAtPrivileged); + } + +} diff --git a/luni-kernel/src/main/java/java/lang/ClassCache.java b/luni-kernel/src/main/java/java/lang/ClassCache.java new file mode 100644 index 0000000..5ea6992 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ClassCache.java @@ -0,0 +1,702 @@ +/* + * 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 org.apache.harmony.kernel.vm.LangAccess; +import org.apache.harmony.kernel.vm.ReflectionAccess; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashSet; + +/** + * Cache of per-class data, meant to help the performance of reflection + * methods. + * + * <p><b>Note:</b> None of the methods perform access checks. It is up + * to the (package internal) clients of this code to perform such + * checks as necessary.</p> + * + * <p><b>Also Note:</b> None of the returned array values are + * protected in any way. It is up to the (again, package internal) + * clients of this code to protect the arrays if they should ever + * escape the package.</p> + */ +/*package*/ class ClassCache<T> { + // TODO: Add caching for constructors and fields. + + /** non-null; comparator used for enumerated values */ + private static final EnumComparator ENUM_COMPARATOR = + new EnumComparator(); + + /** non-null; reflection access bridge */ + /*package*/ static final ReflectionAccess REFLECT = getReflectionAccess(); + + /** non-null; class that this instance represents */ + private final Class<T> clazz; + + /** null-ok; list of all declared methods */ + private volatile Method[] declaredMethods; + + /** null-ok; list of all public declared methods */ + private volatile Method[] declaredPublicMethods; + + /** null-ok; list of all methods, both direct and inherited */ + private volatile Method[] allMethods; + + /** null-ok; list of all public methods, both direct and inherited */ + private volatile Method[] allPublicMethods; + + /** null-ok; list of all declared fields */ + private volatile Field[] declaredFields; + + /** null-ok; list of all public declared fields */ + private volatile Field[] declaredPublicFields; + + /** null-ok; list of all fields, both direct and inherited */ + private volatile Field[] allFields; + + /** null-ok; list of all public fields, both direct and inherited */ + private volatile Field[] allPublicFields; + + /** + * null-ok; array of enumerated values in their original order, if this + * instance's class is an enumeration + */ + private volatile T[] enumValuesInOrder; + + /** + * null-ok; array of enumerated values sorted by name, if this + * instance's class is an enumeration + */ + private volatile T[] enumValuesByName; + + static { + /* + * Provide access to this package from java.util as part of + * bootstrap. TODO: See if this can be removed in favor of the + * simpler mechanism below. (That is, see if EnumSet will be + * happy calling LangAccess.getInstance().) + */ + Field field; + + try { + field = EnumSet.class.getDeclaredField("LANG_BOOTSTRAP"); + REFLECT.setAccessibleNoCheck(field, true); + } catch (NoSuchFieldException ex) { + // This shouldn't happen because the field is in fact defined. + throw new AssertionError(ex); + } + + try { + field.set(null, LangAccessImpl.THE_ONE); + } catch (IllegalAccessException ex) { + // This shouldn't happen because we made the field accessible. + throw new AssertionError(ex); + } + + // Also set up the bootstrap-classpath-wide access mechanism. + LangAccess.setInstance(LangAccessImpl.THE_ONE); + } + + /** + * Constructs an instance. + * + * @param clazz non-null; class that this instance represents + */ + /*package*/ ClassCache(Class<T> clazz) { + if (clazz == null) { + throw new NullPointerException("clazz == null"); + } + + this.clazz = clazz; + this.declaredMethods = null; + this.declaredPublicMethods = null; + this.allMethods = null; + this.allPublicMethods = null; + this.enumValuesInOrder = null; + this.enumValuesByName = null; + this.declaredFields = null; + this.declaredPublicFields = null; + this.allFields = null; + this.allPublicFields = null; + } + + /** + * Gets the list of all declared methods. + * + * @return non-null; the list of all declared methods + */ + public Method[] getDeclaredMethods() { + if (declaredMethods == null) { + declaredMethods = Class.getDeclaredMethods(clazz, false); + } + + return declaredMethods; + } + + /** + * Gets the list of all declared public methods. + * + * @return non-null; the list of all declared public methods + */ + public Method[] getDeclaredPublicMethods() { + if (declaredPublicMethods == null) { + declaredPublicMethods = Class.getDeclaredMethods(clazz, true); + } + + return declaredPublicMethods; + } + + /** + * Gets either the list of declared methods or the list of declared + * public methods. + * + * @param publicOnly whether to only return public methods + */ + public Method[] getDeclaredMethods(boolean publicOnly) { + return publicOnly ? getDeclaredPublicMethods() : getDeclaredMethods(); + } + + /** + * Gets the list of all methods, both directly + * declared and inherited. + * + * @return non-null; the list of all methods + */ + public Method[] getAllMethods() { + if (allMethods == null) { + allMethods = getFullListOfMethods(false); + } + + return allMethods; + } + + /** + * Gets the list of all public methods, both directly + * declared and inherited. + * + * @return non-null; the list of all public methods + */ + public Method[] getAllPublicMethods() { + if (allPublicMethods == null) { + allPublicMethods = getFullListOfMethods(true); + } + + return allPublicMethods; + } + + /* + * Returns the list of methods without performing any security checks + * first. This includes the methods inherited from superclasses. If no + * methods exist at all, an empty array is returned. + * + * @param publicOnly reflects whether we want only public methods + * or all of them + * @return the list of methods + */ + private Method[] getFullListOfMethods(boolean publicOnly) { + ArrayList<Method> methods = new ArrayList<Method>(); + HashSet<String> seen = new HashSet<String>(); + + findAllMethods(clazz, methods, seen, publicOnly); + + return methods.toArray(new Method[methods.size()]); + } + + /** + * Collects the list of methods without performing any security checks + * first. This includes the methods inherited from superclasses and from + * all implemented interfaces. The latter may also implement multiple + * interfaces, so we (potentially) recursively walk through a whole tree of + * classes. If no methods exist at all, an empty array is returned. + * + * @param clazz non-null; class to inspect + * @param methods non-null; the target list to add the results to + * @param seen non-null; a set of signatures we've already seen + * @param publicOnly reflects whether we want only public methods + * or all of them + */ + private static void findAllMethods(Class<?> clazz, + ArrayList<Method> methods, HashSet<String> seen, + boolean publicOnly) { + StringBuilder builder = new StringBuilder(); + Class<?> origClass = clazz; + + // Traverse class and superclasses, get rid of dupes by signature + while (clazz != null) { + Method[] declaredMethods = + clazz.getClassCache().getDeclaredMethods(publicOnly); + int length = declaredMethods.length; + if (length != 0) { + for (Method method : declaredMethods) { + builder.setLength(0); + builder.append(method.getName()); + builder.append('('); + Class<?>[] types = method.getParameterTypes(); + if (types.length != 0) { + builder.append(types[0].getName()); + for (int j = 1; j < types.length; j++) { + builder.append(','); + builder.append(types[j].getName()); + } + } + builder.append(')'); + + String signature = builder.toString(); + if (!seen.contains(signature)) { + methods.add(method); + seen.add(signature); + } + } + } + + clazz = clazz.getSuperclass(); + } + + // Traverse all interfaces, and do the same recursively. + Class<?>[] interfaces = origClass.getInterfaces(); + for (Class<?> intf : interfaces) { + findAllMethods(intf, methods, seen, publicOnly); + } + } + + /** + * Finds and returns a method with a given name and signature. Use + * this with one of the method lists returned by instances of this class. + * + * @param list non-null; the list of methods to search through + * @param parameterTypes non-null; the formal parameter list + * @return non-null; the matching method + * @throws NoSuchMethodException thrown if the method does not exist + */ + public static Method findMethodByName(Method[] list, String name, + Class<?>[] parameterTypes) throws NoSuchMethodException { + if (name == null) { + throw new NullPointerException("Method name must not be null."); + } + for (int i = list.length - 1; i >= 0; i--) { + Method method = list[i]; + if (method.getName().equals(name) + && compareClassLists( + method.getParameterTypes(), parameterTypes)) { + return method; + } + } + + throw new NoSuchMethodException(name); + } + + /** + * Compares two class lists for equality. Empty and + * <code>null</code> lists are considered equal. This is useful + * for matching methods and constructors. + * + * <p>TODO: Take into account assignment compatibility?</p> + * + * @param a null-ok; the first list of types + * @param b null-ok; the second list of types + * @return true if and only if the lists are equal + */ + public static boolean compareClassLists(Class<?>[] a, Class<?>[] b) { + if (a == null) { + return (b == null) || (b.length == 0); + } + + int length = a.length; + + if (b == null) { + return (length == 0); + } + + if (length != b.length) { + return false; + } + + for (int i = length - 1; i >= 0; i--) { + if (a[i] != b[i]) { + return false; + } + } + + return true; + } + + /** + * Makes a deep copy of the given array of methods. This is useful + * when handing out arrays from the public API. + * + * <p><b>Note:</b> In such cases, it is insufficient to just make + * a shallow copy of the array, since method objects aren't + * immutable due to the existence of {@link + * AccessibleObject#setAccessible}.</p> + * + * @param orig non-null; array to copy + * @return non-null; a deep copy of the given array + */ + public static Method[] deepCopy(Method[] orig) { + int length = orig.length; + Method[] result = new Method[length]; + + for (int i = length - 1; i >= 0; i--) { + result[i] = REFLECT.clone(orig[i]); + } + + return result; + } + + /** + * Gets the list of all declared fields. + * + * @return non-null; the list of all declared fields + */ + public Field[] getDeclaredFields() { + if (declaredFields == null) { + declaredFields = Class.getDeclaredFields(clazz, false); + } + + return declaredFields; + } + + /** + * Gets the list of all declared public fields. + * + * @return non-null; the list of all declared public fields + */ + public Field[] getDeclaredPublicFields() { + if (declaredPublicFields == null) { + declaredPublicFields = Class.getDeclaredFields(clazz, true); + } + + return declaredPublicFields; + } + + /** + * Gets either the list of declared fields or the list of declared + * public fields. + * + * @param publicOnly whether to only return public fields + */ + public Field[] getDeclaredFields(boolean publicOnly) { + return publicOnly ? getDeclaredPublicFields() : getDeclaredFields(); + } + + /** + * Gets the list of all fields, both directly + * declared and inherited. + * + * @return non-null; the list of all fields + */ + public Field[] getAllFields() { + if (allFields == null) { + allFields = getFullListOfFields(false); + } + + return allFields; + } + + /** + * Gets the list of all public fields, both directly + * declared and inherited. + * + * @return non-null; the list of all public fields + */ + public Field[] getAllPublicFields() { + if (allPublicFields == null) { + allPublicFields = getFullListOfFields(true); + } + + return allPublicFields; + } + + /* + * Returns the list of fields without performing any security checks + * first. This includes the fields inherited from superclasses. If no + * fields exist at all, an empty array is returned. + * + * @param publicOnly reflects whether we want only public fields + * or all of them + * @return the list of fields + */ + private Field[] getFullListOfFields(boolean publicOnly) { + ArrayList<Field> fields = new ArrayList<Field>(); + HashSet<String> seen = new HashSet<String>(); + + findAllfields(clazz, fields, seen, publicOnly); + + return fields.toArray(new Field[fields.size()]); + } + + /** + * Collects the list of fields without performing any security checks + * first. This includes the fields inherited from superclasses and from + * all implemented interfaces. The latter may also implement multiple + * interfaces, so we (potentially) recursively walk through a whole tree of + * classes. If no fields exist at all, an empty array is returned. + * + * @param clazz non-null; class to inspect + * @param fields non-null; the target list to add the results to + * @param seen non-null; a set of signatures we've already seen + * @param publicOnly reflects whether we want only public fields + * or all of them + */ + private static void findAllfields(Class<?> clazz, + ArrayList<Field> fields, HashSet<String> seen, + boolean publicOnly) { + + // Traverse class and superclasses, get rid of dupes by signature + while (clazz != null) { + Field[] declaredFields = + clazz.getClassCache().getDeclaredFields(publicOnly); + for (Field field : declaredFields) { + String signature = field.toString(); + if (!seen.contains(signature)) { + fields.add(field); + seen.add(signature); + } + } + + // Traverse all interfaces, and do the same recursively. + Class<?>[] interfaces = clazz.getInterfaces(); + for (Class<?> intf : interfaces) { + findAllfields(intf, fields, seen, publicOnly); + } + + clazz = clazz.getSuperclass(); + } + } + + /** + * Finds and returns a field with a given name and signature. Use + * this with one of the field lists returned by instances of this class. + * + * @param list non-null; the list of fields to search through + * @return non-null; the matching field + * @throws NoSuchFieldException thrown if the field does not exist + */ + public static Field findFieldByName(Field[] list, String name) + throws NoSuchFieldException { + if (name == null) { + throw new NullPointerException("Field name must not be null."); + } + for (int i = 0; i < list.length; i++) { + Field field = list[i]; + if (field.getName().equals(name)) { + return field; + } + } + + throw new NoSuchFieldException(name); + } + + /** + * Makes a deep copy of the given array of fields. This is useful + * when handing out arrays from the public API. + * + * <p><b>Note:</b> In such cases, it is insufficient to just make + * a shallow copy of the array, since field objects aren't + * immutable due to the existence of {@link + * AccessibleObject#setAccessible}.</p> + * + * @param orig non-null; array to copy + * @return non-null; a deep copy of the given array + */ + public static Field[] deepCopy(Field[] orig) { + int length = orig.length; + Field[] result = new Field[length]; + + for (int i = length - 1; i >= 0; i--) { + result[i] = REFLECT.clone(orig[i]); + } + + return result; + } + + /** + * Gets the enumerated value with a given name. + * + * @param name non-null; name of the value + * @return null-ok; the named enumerated value or <code>null</code> + * if this instance's class doesn't have such a value (including + * if this instance isn't in fact an enumeration) + */ + @SuppressWarnings("unchecked") + public T getEnumValue(String name) { + Enum[] values = (Enum[]) getEnumValuesByName(); + + if (values == null) { + return null; + } + + // Binary search. + + int min = 0; + int max = values.length - 1; + + while (min <= max) { + /* + * The guessIdx calculation is equivalent to ((min + max) + * / 2) but won't go wonky when min and max are close to + * Integer.MAX_VALUE. + */ + int guessIdx = min + ((max - min) >> 1); + Enum guess = values[guessIdx]; + int cmp = name.compareTo(guess.name()); + + if (cmp < 0) { + max = guessIdx - 1; + } else if (cmp > 0) { + min = guessIdx + 1; + } else { + return (T) guess; + } + } + + return null; + } + + /** + * Gets the array of enumerated values, sorted by name. + * + * @return null-ok; the value array, or <code>null</code> if this + * instance's class isn't in fact an enumeration + */ + public T[] getEnumValuesByName() { + if (enumValuesByName == null) { + T[] values = getEnumValuesInOrder(); + + if (values != null) { + values = (T[]) values.clone(); + Arrays.sort((Enum<?>[]) values, ENUM_COMPARATOR); + + /* + * Note: It's only safe (concurrency-wise) to set the + * instance variable after the array is properly sorted. + */ + enumValuesByName = values; + } + } + + return enumValuesByName; + } + + /** + * Gets the array of enumerated values, in their original declared + * order. + * + * @return null-ok; the value array, or <code>null</code> if this + * instance's class isn't in fact an enumeration + */ + public T[] getEnumValuesInOrder() { + if ((enumValuesInOrder == null) && clazz.isEnum()) { + enumValuesInOrder = callEnumValues(); + } + + return enumValuesInOrder; + } + + /** + * Calls the static method <code>values()</code> on this + * instance's class, which is presumed to be a properly-formed + * enumeration class, using proper privilege hygiene. + * + * @return non-null; the array of values as reported by + * <code>value()</code> + */ + @SuppressWarnings("unchecked") + private T[] callEnumValues() { + Method method; + + try { + Method[] methods = getDeclaredPublicMethods(); + method = findMethodByName(methods, "values", (Class[]) null); + method = REFLECT.accessibleClone(method); + } catch (NoSuchMethodException ex) { + // This shouldn't happen if the class is a well-formed enum. + throw new UnsupportedOperationException(ex); + } + + try { + return (T[]) method.invoke((Object[]) null); + } catch (IllegalAccessException ex) { + // This shouldn't happen because the method is "accessible." + throw new Error(ex); + } catch (InvocationTargetException ex) { + Throwable te = ex.getTargetException(); + if (te instanceof RuntimeException) { + throw (RuntimeException) te; + } else if (te instanceof Error) { + throw (Error) te; + } else { + throw new Error(te); + } + } + } + + /** + * Gets the reflection access object. This uses reflection to do + * so. My head is spinning. + * + * @return non-null; the reflection access object + */ + private static ReflectionAccess getReflectionAccess() { + /* + * Note: We can't do AccessibleObject.class.getCache() to + * get the cache, since that would cause a circularity in + * initialization. So instead, we do a direct call into the + * native side. + */ + Method[] methods = + Class.getDeclaredMethods(AccessibleObject.class, false); + + try { + Method method = findMethodByName(methods, "getReflectionAccess", + (Class[]) null); + Class.setAccessibleNoCheck(method, true); + return (ReflectionAccess) method.invoke((Object[]) null); + } catch (NoSuchMethodException ex) { + /* + * This shouldn't happen because the method + * AccessibleObject.getReflectionAccess() really is defined + * in this module. + */ + throw new Error(ex); + } catch (IllegalAccessException ex) { + // This shouldn't happen because the method is "accessible." + throw new Error(ex); + } catch (InvocationTargetException ex) { + throw new Error(ex); + } + } + + /** + * Comparator class for enumerated values. It compares strictly + * by name. + */ + private static class EnumComparator implements Comparator<Enum<?>> { + public int compare(Enum<?> e1, Enum<?> e2) { + return e1.name().compareTo(e2.name()); + } + } +} diff --git a/luni-kernel/src/main/java/java/lang/ClassLoader.java b/luni-kernel/src/main/java/java/lang/ClassLoader.java new file mode 100644 index 0000000..822fade --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ClassLoader.java @@ -0,0 +1,1139 @@ +/* + * 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 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.Enumeration; +import java.util.Map; +import java.util.HashMap; + +import dalvik.system.PathClassLoader; +import dalvik.system.VMStack; + +/** + * 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> + * + * @since Android 1.0 + * @see Class + */ +public abstract class ClassLoader { + + // BEGIN android-note + /* + * 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. + */ + // END android-note + /** + * 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. + * + * @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. If a security manager is present and the caller's + * class loader is neither {@code null} nor the same as or an ancestor of + * the system class loader, then this method calls the security manager's + * checkPermission method with a RuntimePermission("getClassLoader") + * permission to ensure that it is ok to access the system class loader. If + * not, a {@code SecurityException} is thrown. + * + * @return the system class loader. + * @throws SecurityException + * if a security manager exists and it does not allow access to + * the system class loader. + * @since Android 1.0 + */ + public static ClassLoader getSystemClassLoader() { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + ClassLoader caller = VMStack.getCallingClassLoader(); + if (caller != null && !caller.isAncestorOf(SystemClassLoader.loader)) { + smgr.checkPermission(new RuntimePermission("getClassLoader")); + } + } + + 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 + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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. + * + * @throws SecurityException + * if a security manager exists and it does not allow the + * creation of a new {@code ClassLoader}. + * @since Android 1.0 + */ + 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. + * @throws SecurityException + * if a security manager exists and it does not allow the + * creation of new a new {@code ClassLoader}. + * @since Android 1.0 + */ + protected ClassLoader(ClassLoader parentLoader) { + this(parentLoader, false); + } + + /* + * constructor for the BootClassLoader which needs parent to be null. + */ + ClassLoader(ClassLoader parentLoader, boolean nullAllowed) { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkCreateClassLoader(); + } + + if (parentLoader == null && !nullAllowed) { + throw new NullPointerException( + "Parent ClassLoader may not be null"); + } + + 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)} + * @since Android 1.0 + */ + @Deprecated + protected final Class<?> defineClass(byte[] classRep, int offset, int length) + throws ClassFormatError { + + return VMClassLoader.defineClass(this, classRep, offset, length, null); + } + + /** + * 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}. + * @since Android 1.0 + */ + protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length) + throws ClassFormatError { + + // TODO Define a default ProtectionDomain on first use + return defineClass(className, classRep, offset, length, null); + } + + /** + * 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}. + * @since Android 1.0 + */ + protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length, + ProtectionDomain protectionDomain) throws java.lang.ClassFormatError { + + return VMClassLoader.defineClass(this, className, classRep, offset, length, + protectionDomain); + } + + /** + * 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}. + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + 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 virtual machine 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. + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + 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}. + * @throws SecurityException + * if a security manager exists and it does not allow to + * retrieve the parent class loader. + * @since Android 1.0 + */ + public final ClassLoader getParent() { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkPermission(new RuntimePermission("getClassLoader")); + } + + 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 either the resource can not be found or a security manager + * does not allow to access the resource. + * @see Class#getResource + * @since Android 1.0 + */ + public URL getResource(String resName) { + URL resource = null; + + 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. + * @since Android 1.0 + */ + @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 either the resource + * can not be found or a security manager does not allow to access + * the resource. + * @param resName + * the name of the resource to find. + * @see Class#getResourceAsStream + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + protected final void resolveClass(Class<?> clazz) { + // no-op, doesn't make sense on android. + } + + /** + * Indicates whether this class loader is the system class loader. This + * method must be provided by the virtual machine vendor, as it is used by + * other provided class implementations in this package. A sample + * implementation of this method is provided by the reference + * implementation. This method is used by + * SecurityManager.classLoaderDepth(), currentClassLoader() and + * currentLoadedClass(). Returns true if the receiver is a system class + * loader. + * <p> + * Note that this method has package visibility only. It is defined here to + * avoid the security manager check in getSystemClassLoader, which would be + * required to implement this method anywhere else. + * </p> + * + * @return {@code true} if the receiver is a system class loader + * @see Class#getClassLoaderImpl() + */ + final boolean isSystemClassLoader() { + return false; + } + + /** + * <p> + * Returns true if the receiver is ancestor of another class loader. It also + * returns true if the two class loader are equal. + * </p> + * <p> + * Note that this method has package visibility only. It is defined here to + * avoid the security manager check in getParent, which would be required to + * implement this method anywhere else. The method is also required in other + * places where class loaders are accesses. + * </p> + * + * @param child + * A child candidate + * @return {@code true} if the receiver is ancestor of, or equal to, + * the parameter + */ + final boolean isAncestorOf(ClassLoader child) { + for (ClassLoader current = child; current != null; + current = child.parent) { + if (current == this) { + return true; + } + } + return false; + } + + /** + * 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. + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + @SuppressWarnings( { + "unchecked", "unused" + }) + protected Enumeration<URL> findResources(String resName) throws IOException { + return EmptyEnumeration.getInstance(); + } + + /** + * 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. + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + protected Package getPackage(String name) { + synchronized (packages) { + Package p = packages.get(name); + return p; + } + } + + /** + * Gets the package with the specified name, searching it in the specified + * class loader. + * + * @param loader + * the class loader to search the package in. + * @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. + * @since Android 1.0 + */ + static Package getPackage(ClassLoader loader, String name) { + return loader.getPackage(name); + } + + /** + * Returns all the packages known to this class loader. + * + * @return an array with all packages known to this class loader. + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + 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; + } + } + + /** + * Gets the signers of the specified class. This implementation returns + * {@code null}. + * + * @param c + * the {@code Class} object for which to get the signers. + * @return signers the signers of {@code c}. + * @since Android 1.0 + */ + final Object[] getSigners(Class<?> c) { + return null; + } + + /** + * 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}. + * @since Android 1.0 + */ + protected final void setSigners(Class<?> c, Object[] signers) { + return; + } + + /** + * <p> + * This must be provided by the VM vendor. It is used by + * SecurityManager.checkMemberAccess() with depth = 3. Note that + * checkMemberAccess() assumes the following stack when called:<br> + * </p> + * + * <pre> + * < user code &gt; <- want this class + * Class.getDeclared*(); + * Class.checkMemberAccess(); + * SecurityManager.checkMemberAccess(); <- current frame + * </pre> + * + * <p> + * Returns the ClassLoader of the method (including natives) at the + * specified depth on the stack of the calling thread. Frames representing + * the VM implementation of java.lang.reflect are not included in the list. + * </p> + * Notes: + * <ul> + * <li>This method operates on the defining classes of methods on stack. + * NOT the classes of receivers.</li> + * <li>The item at depth zero is the caller of this method</li> + * </ul> + * + * @param depth + * the stack depth of the requested ClassLoader + * @return the ClassLoader at the specified depth + */ + static final ClassLoader getStackClassLoader(int depth) { + Class<?>[] stack = VMStack.getClasses(depth + 1, false); + if(stack.length < depth + 1) { + return null; + } + return stack[depth].getClassLoader(); + } + + /** + * This method must be provided by the VM vendor, as it is called by + * java.lang.System.loadLibrary(). System.loadLibrary() cannot call + * Runtime.loadLibrary() because this method loads the library using the + * ClassLoader of the calling method. Loads and links the library specified + * by the argument. + * + * @param libName + * the name of the library to load + * @param loader + * the classloader in which to load the library + * @throws UnsatisfiedLinkError + * if the library could not be loaded + * @throws SecurityException + * if the library was not allowed to be loaded + * <p> + * <strong>Note: </strong>This method does nothing in the Android reference + * implementation. + * </p> + */ + static void loadLibraryWithClassLoader(String libName, ClassLoader loader) { + return; + } + + /** + * This method must be provided by the VM vendor, as it is called by + * java.lang.System.load(). System.load() cannot call Runtime.load() because + * the library is loaded using the ClassLoader of the calling method. Loads + * and links the library specified by the argument. No security check is + * done. + * <p> + * <strong>Note: </strong>This method does nothing in the Android reference + * implementation. + * </p> + * + * @param libName + * the name of the library to load + * @param loader + * the classloader in which to load the library + * @param libraryPath + * the library path to search, or null + * @throws UnsatisfiedLinkError + * if the library could not be loaded + */ + static void loadLibraryWithPath(String libName, ClassLoader loader, String libraryPath) { + return; + } + + /** + * 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. + * @since Android 1.0 + */ + public void setClassAssertionStatus(String cname, boolean enable) { + return; + } + + /** + * 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. + * @since Android 1.0 + */ + public void setPackageAssertionStatus(String pname, boolean enable) { + return; + } + + /** + * 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. + * @since Android 1.0 + */ + public void setDefaultAssertionStatus(boolean enable) { + return; + } + + /** + * 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> + * + * @since Android 1.0 + */ + public void clearAssertionStatus() { + return; + } + + /** + * Returns the assertion status of the named class Returns the assertion + * status of the class or nested class if it has been set. Otherwise returns + * the assertion status of its package or superpackage if that has been set. + * Otherwise returns the default assertion status. Returns 1 for enabled and + * 0 for disabled. + * + * @return the assertion status. + * @param cname + * the name of class. + */ + boolean getClassAssertionStatus(String cname) { + return false; + } + + /** + * Returns the assertion status of the named package Returns the assertion + * status of the named package or superpackage if that has been set. + * Otherwise returns the default assertion status. Returns 1 for enabled and + * 0 for disabled. + * + * @return the assertion status. + * @param pname + * the name of package. + */ + boolean getPackageAssertionStatus(String pname) { + return false; + } + + /** + * Returns the default assertion status + * + * @return the default assertion status. + */ + boolean getDefaultAssertionStatus() { + return false; + } +} + +/* + * 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 { + + static BootClassLoader instance; + + public static 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 { + Enumeration<URL> result = VMClassLoader.getResources(resName); + + // VMClassLoader doesn't keep the contract for getResources() + if (result == null) { + result = EmptyEnumeration.getInstance(); + } + + return result; + } + + /** + * 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 && !"".equals(name)) { + 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/luni-kernel/src/main/java/java/lang/Compiler.java b/luni-kernel/src/main/java/java/lang/Compiler.java new file mode 100644 index 0000000..cf41f06 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/Compiler.java @@ -0,0 +1,100 @@ +/* + * 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; + +/** + * Placeholder class for environments which explicitly manage the action of a + * <em>Just In Time (JIT)</em> compiler. This class is usually implemented by + * the virtual machine vendor. The Android reference implementation does not + * (yet) contain such a JIT compiler, though other implementations may choose to + * provide one. + * + * @since Android 1.0 + */ +public final class Compiler { + + /** + * Prevent this class from being instantiated. + */ + private Compiler(){ + //do nothing + } + + /** + * Executes an operation according to the specified command object. This + * method is the low-level interface to the JIT compiler. It may return any + * object or {@code null} if no JIT compiler is available. + * + * @param cmd + * the command object for the JIT compiler. + * @return the result of executing command or {@code null}. + * @since Android 1.0 + */ + public static Object command(Object cmd) { + return null; + } + + /** + * Compiles the specified class using the JIT compiler and indicates if + * compilation has been successful. + * + * @param classToCompile + * java.lang.Class the class to JIT compile + * @return {@code true} if the compilation has been successful; + * {@code false} if it has failed or if there is no JIT compiler + * available. + * @since Android 1.0 + */ + public static boolean compileClass(Class<?> classToCompile) { + return false; + } + + /** + * Compiles all classes whose name matches the specified name using the JIT + * compiler and indicates if compilation has been successful. + * + * @param nameRoot + * the string to match class names with. + * @return {@code true} if the compilation has been successful; + * {@code false} if it has failed or if there is no JIT compiler + * available. + * @since Android 1.0 + */ + public static boolean compileClasses(String nameRoot) { + return false; + } + + /** + * Disables the JIT compiler. + * + * @since Android 1.0 + */ + public static void disable() { + return; + } + + /** + * Enables the JIT compiler. + * + * @since Android 1.0 + */ + public static void enable() { + return; + } + +} diff --git a/luni-kernel/src/main/java/java/lang/LangAccessImpl.java b/luni-kernel/src/main/java/java/lang/LangAccessImpl.java new file mode 100644 index 0000000..44d4aac --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/LangAccessImpl.java @@ -0,0 +1,55 @@ +/* + * 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 org.apache.harmony.kernel.vm.LangAccess; + +/** + * Implementation of bridge into <code>java.lang</code>. + */ +/*package*/ final class LangAccessImpl extends LangAccess { + /** non-null; unique instance of this class */ + /*package*/ static final LangAccessImpl THE_ONE = new LangAccessImpl(); + + /** + * This class is not publicly instantiable. Use {@link #THE_ONE}. + */ + private LangAccessImpl() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + public <T> T[] getEnumValuesInOrder(Class<T> clazz) { + ClassCache<T> cache = clazz.getClassCache(); + return cache.getEnumValuesInOrder(); + } + + /** {@inheritDoc} */ + public void unpark(Thread thread) { + thread.unpark(); + } + + /** {@inheritDoc} */ + public void parkFor(long nanos) { + Thread.currentThread().parkFor(nanos); + } + + /** {@inheritDoc} */ + public void parkUntil(long time) { + Thread.currentThread().parkUntil(time); + } +} diff --git a/luni-kernel/src/main/java/java/lang/Object.java b/luni-kernel/src/main/java/java/lang/Object.java new file mode 100644 index 0000000..4fef609 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/Object.java @@ -0,0 +1,368 @@ +/* + * 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. + * <p> + * {@code Object} provides some fundamental methods for accessing the + * {@link Class} of an object, getting its {@link #hashCode()}, or checking + * whether one object {@link #equals(Object)} another. The {@link #toString()} + * method can be used to convert an object reference into a printable string and + * is often overridden in subclasses. + * <p> + * The {@link #wait()} and {@link #notify()} methods provide a foundation for + * synchronization, acquiring and releasing an internal monitor associated with + * each {@code Object}. + * + * @since Android 1.0 + */ +public class Object { + + /** + * Constructs a new instance of {@code Object}. + * + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + 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 both transitive and reflexive. + * <p> + * The implementation in {@code Object} returns {@code true} only if {@code + * o} is the exact same object as the receiver (using the == operator for + * comparison). Subclasses often implement {@code equals(Object)} so that + * it takes into account the two object's types and states. + * </p> + * <p> + * The general contract for the {@code equals(Object)} 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 none of them. + * </p> + * + * @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 + * @since Android 1.0 + */ + public boolean equals(Object o) { + return this == o; + } + + /** + * Is called before the object's memory is being reclaimed by the VM. This + * can only happen once the VM has detected, during a run of the garbage + * collector, that the object is no longer reachable by any thread of the + * running application. + * <p> + * The method can be used to free system resources or perform other cleanup + * before the object is garbage collected. The default implementation of the + * method is empty, which is also expected by the VM, but subclasses can + * override {@code finalize()} as required. Uncaught exceptions which are + * thrown during the execution of this method cause it to terminate + * immediately but are otherwise ignored. + * <p> + * Note that the VM does guarantee that {@code finalize()} is called at most + * once for any object, but it doesn't guarantee when (if at all) {@code + * finalize()} will be called. For example, object B's {@code finalize()} + * can delay the execution of object A's {@code finalize()} method and + * therefore it can delay the reclamation of A's memory. To be safe, use a + * {@link java.lang.ref.ReferenceQueue}, because it provides more control + * over the way the VM deals with references during garbage collection. + * </p> + * + * @throws Throwable + * any exception which is raised during finalization; these are + * ignored by the virtual machine. + * @since Android 1.0 + */ + protected void finalize() throws Throwable { + } + + /** + * Returns the unique instance of {@link Class} which 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 expression {@code getClass()} was called upon. + * <p> + * As an example, the following code actually compiles, although one might + * think it shouldn't: + * <p> + * <pre> + * List<Integer> l = new ArrayList<Integer>(); + * Class<? extends List> c = l.getClass(); + * </pre> + * + * @return this object's {@code Class} instance. + * @since Android 1.0 + */ + public final native Class<? extends Object> getClass(); + + /** + * Returns an integer hash code for this object. By contract, any two + * objects for which {@code equals(Object)} returns {@code true} must return + * the same hash code value. This means that subclasses of {@code Object} + * usually override both methods or neither method. + * + * @return this object's hash code. + * @see #equals + * @since Android 1.0 + */ + 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 + * virtual machine. 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 + * @since Android 1.0 + */ + 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 simply concatenates the class name, the '@' sign + * and a hexadecimal representation of the object's {@link #hashCode()}, + * that is, it is equivalent to the following expression: + * + * <pre> + * getClass().getName() + '@' + Integer.toHexString(hashCode()) + * </pre> + * + * @return a printable representation of this object. + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + public final native void wait(long millis, int nanos) throws InterruptedException; +} diff --git a/luni-kernel/src/main/java/java/lang/Package.java b/luni-kernel/src/main/java/java/lang/Package.java new file mode 100644 index 0000000..2817404 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/Package.java @@ -0,0 +1,321 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.net.URL; + +/** + * Contains information about a Java package. This includes implementation and + * specification versions. Typically this information is retrieved from the + * manifest. + * <p> + * Packages are managed by class loaders. All classes loaded by the same loader + * from the same package share a {@code Package} instance. + * </p> + * @since Android 1.0 + * + * @see java.lang.ClassLoader + */ +public class Package implements AnnotatedElement { + + private final String name, specTitle, specVersion, specVendor, implTitle, + implVersion, implVendor; + private final URL sealBase; + + Package(String name, String specTitle, String specVersion, String specVendor, + String implTitle, String implVersion, String implVendor, URL sealBase) { + this.name = name; + this.specTitle = specTitle; + this.specVersion = specVersion; + this.specVendor = specVendor; + this.implTitle = implTitle; + this.implVersion = implVersion; + this.implVendor = implVendor; + this.sealBase = sealBase; + } + + /** + * Gets the annotation associated with the specified annotation type and + * this package, if present. + * + * @param annotationType + * the annotation type to look for. + * @return an instance of {@link Annotation} or {@code null}. + * @see java.lang.reflect.AnnotatedElement#getAnnotation(java.lang.Class) + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + public <T extends Annotation> T getAnnotation(Class<T> annotationType) { + Annotation[] list = getAnnotations(); + for (int i = 0; i < list.length; i++) { + if (annotationType.isInstance(list[i])) { + return (T)list[i]; + } + } + + return null; + } + + /** + * Gets all annotations associated with this package, if any. + * + * @return an array of {@link Annotation} instances, which may be empty. + * @see java.lang.reflect.AnnotatedElement#getAnnotations() + * @since Android 1.0 + */ + public Annotation[] getAnnotations() { + return getDeclaredAnnotations(this, true); + } + + /** + * Gets all annotations directly declared on this package, if any. + * + * @return an array of {@link Annotation} instances, which may be empty. + * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations() + * @since Android 1.0 + */ + public Annotation[] getDeclaredAnnotations() { + return getDeclaredAnnotations(this, false); + } + + /* + * Returns the list of declared annotations of the given package. + * If no annotations exist, an empty array is returned. + * + * @param pkg the package of interest + * @param publicOnly reflects whether we want only public annotation or all + * of them. + * @return the list of annotations + */ + // TODO(Google) Provide proper (native) implementation. + private static native Annotation[] getDeclaredAnnotations(Package pkg, + boolean publicOnly); + + /** + * Indicates whether the specified annotation is present. + * + * @param annotationType + * the annotation type to look for. + * @return {@code true} if the annotation is present; {@code false} + * otherwise. + * @see java.lang.reflect.AnnotatedElement#isAnnotationPresent(java.lang.Class) + * @since Android 1.0 + */ + public boolean isAnnotationPresent( + Class<? extends Annotation> annotationType) { + return getAnnotation(annotationType) != null; + } + + /** + * Returns the title of the implementation of this package, or {@code null} + * if this is unknown. The format of this string is unspecified. + * + * @return the implementation title, may be {@code null}. + * @since Android 1.0 + */ + public String getImplementationTitle() { + return implTitle; + } + + /** + * Returns the name of the vendor or organization that provides this + * implementation of the package, or {@code null} if this is unknown. The + * format of this string is unspecified. + * + * @return the implementation vendor name, may be {@code null}. + * @since Android 1.0 + */ + public String getImplementationVendor() { + return implVendor; + } + + /** + * Returns the version of the implementation of this package, or {@code + * null} if this is unknown. The format of this string is unspecified. + * + * @return the implementation version, may be {@code null}. + * @since Android 1.0 + */ + public String getImplementationVersion() { + return implVersion; + } + + /** + * Returns the name of this package in the standard dot notation; for + * example: "java.lang". + * + * @return the name of this package. + * @since Android 1.0 + */ + public String getName() { + return name; + } + + /** + * Attempts to locate the requested package in the caller's class loader. If + * no package information can be located, {@code null} is returned. + * + * @param packageName + * the name of the package to find. + * @return the requested package, or {@code null}. + * @see ClassLoader#getPackage(java.lang.String) + * @since Android 1.0 + */ + public static Package getPackage(String packageName) { + ClassLoader classloader = VMStack.getCallingClassLoader(); + return classloader.getPackage(packageName); + } + + /** + * Returns all the packages known to the caller's class loader. + * + * @return all the packages known to the caller's class loader. + * @see ClassLoader#getPackages + * @since Android 1.0 + */ + public static Package[] getPackages() { + ClassLoader classloader = VMStack.getCallingClassLoader(); + return classloader.getPackages(); + } + + /** + * Returns the title of the specification this package implements, or + * {@code null} if this is unknown. + * + * @return the specification title, may be {@code null}. + * @since Android 1.0 + */ + public String getSpecificationTitle() { + return specTitle; + } + + /** + * Returns the name of the vendor or organization that owns and maintains + * the specification this package implements, or {@code null} if this is + * unknown. + * + * @return the specification vendor name, may be {@code null}. + * @since Android 1.0 + */ + public String getSpecificationVendor() { + return specVendor; + } + + /** + * Returns the version of the specification this package implements, or + * {@code null} if this is unknown. The version string is a sequence of + * non-negative integers separated by dots; for example: "1.2.3". + * + * @return the specification version string, may be {@code null}. + * @since Android 1.0 + */ + public String getSpecificationVersion() { + return specVersion; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + /** + * Indicates whether this package's specification version is compatible with + * the specified version string. Version strings are compared by comparing + * each dot separated part of the version as an integer. + * + * @param version + * the version string to compare against. + * @return {@code true} if the package versions are compatible; {@code + * false} otherwise. + * @throws NumberFormatException + * if this package's version string or the one provided are not + * in the correct format. + * @since Android 1.0 + */ + public boolean isCompatibleWith(String version) + throws NumberFormatException { + String[] requested = version.split("."); + String[] provided = specVersion.split("."); + + for (int i = 0; i < Math.min(requested.length, provided.length); i++) { + int reqNum = Integer.parseInt(requested[i]); + int provNum = Integer.parseInt(provided[i]); + + if (reqNum > provNum) { + return false; + } else if (reqNum < provNum) { + return true; + } + } + + if (requested.length > provided.length) { + return false; + } + + return true; + } + + /** + * Indicates whether this package is sealed. + * + * @return {@code true} if this package is sealed; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isSealed() { + return sealBase != null; + } + + /** + * Indicates whether this package is sealed with respect to the specified + * URL. + * + * @param url + * the URL to check. + * @return {@code true} if this package is sealed with {@code url}; {@code + * false} otherwise + * @since Android 1.0 + */ + public boolean isSealed(URL url) { + return sealBase != null && sealBase.sameFile(url); + } + + @Override + public String toString() { + return "package " + name; + } +} + diff --git a/luni-kernel/src/main/java/java/lang/ProcessManager.java b/luni-kernel/src/main/java/java/lang/ProcessManager.java new file mode 100644 index 0000000..1e21b57 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ProcessManager.java @@ -0,0 +1,384 @@ +/* + * 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.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.Arrays; +import java.util.logging.Logger; +import java.util.logging.Level; + +/** + * Manages child processes. + * + * <p>Harmony's native implementation (for comparison purposes): + * http://tinyurl.com/3ytwuq + */ +final class ProcessManager { + + /** + * constant communicated from native code indicating that a + * child died, but it was unable to determine the status + */ + private static final int WAIT_STATUS_UNKNOWN = -1; + + /** + * constant communicated from native code indicating that there + * are currently no children to wait for + */ + private static final int WAIT_STATUS_NO_CHILDREN = -2; + + /** + * constant communicated from native code indicating that a wait() + * call returned -1 and set an undocumented (and hence unexpected) errno + */ + private static final int WAIT_STATUS_STRANGE_ERRNO = -3; + + /** + * Initializes native static state. + */ + static native void staticInitialize(); + static { + staticInitialize(); + } + + /** + * Map from pid to Process. We keep weak references to the Process objects + * and clean up the entries when no more external references are left. The + * process objects themselves don't require much memory, but file + * descriptors (associated with stdin/out/err in this case) can be + * a scarce resource. + */ + private final Map<Integer, ProcessReference> processReferences + = new HashMap<Integer, ProcessReference>(); + + /** Keeps track of garbage-collected Processes. */ + private final ProcessReferenceQueue referenceQueue + = new ProcessReferenceQueue(); + + private ProcessManager() { + // Spawn a thread to listen for signals from child processes. + Thread processThread = new Thread(ProcessManager.class.getName()) { + @Override + public void run() { + watchChildren(); + } + }; + processThread.setDaemon(true); + processThread.start(); + } + + /** + * Kills the process with the given ID. + * + * @parm pid ID of process to kill + */ + private static native void kill(int pid) throws IOException; + + /** + * Cleans up after garbage collected processes. Requires the lock on the + * map. + */ + void cleanUp() { + ProcessReference reference; + while ((reference = referenceQueue.poll()) != null) { + synchronized (processReferences) { + processReferences.remove(reference.processId); + } + } + } + + /** + * Listens for signals from processes and calls back to + * {@link #onExit(int,int)}. + */ + native void watchChildren(); + + /** + * Called by {@link #watchChildren()} when a child process exits. + * + * @param pid ID of process that exited + * @param exitValue value the process returned upon exit + */ + void onExit(int pid, int exitValue) { + ProcessReference processReference = null; + + synchronized (processReferences) { + cleanUp(); + if (pid >= 0) { + processReference = processReferences.remove(pid); + } else if (exitValue == WAIT_STATUS_NO_CHILDREN) { + if (processReferences.isEmpty()) { + /* + * There are no eligible children; wait for one to be + * added. The wait() will return due to the + * notifyAll() call below. + */ + try { + processReferences.wait(); + } catch (InterruptedException ex) { + // This should never happen. + throw new AssertionError("unexpected interrupt"); + } + } else { + /* + * A new child was spawned just before we entered + * the synchronized block. We can just fall through + * without doing anything special and land back in + * the native wait(). + */ + } + } else { + // Something weird is happening; abort! + throw new AssertionError("unexpected wait() behavior"); + } + } + + if (processReference != null) { + ProcessImpl process = processReference.get(); + if (process != null) { + process.setExitValue(exitValue); + } + } + } + + /** + * Executes a native process. Fills in in, out, and err and returns the + * new process ID upon success. + */ + static native int exec(String[] commands, String[] environment, + String workingDirectory, FileDescriptor in, FileDescriptor out, + FileDescriptor err) throws IOException; + + /** + * Executes a process and returns an object representing it. + */ + Process exec(String[] commands, String[] environment, + File workingDirectory) throws IOException { + FileDescriptor in = new FileDescriptor(); + FileDescriptor out = new FileDescriptor(); + FileDescriptor err = new FileDescriptor(); + + String workingPath = (workingDirectory == null) + ? null + : workingDirectory.getPath(); + + // Ensure onExit() doesn't access the process map before we add our + // entry. + synchronized (processReferences) { + int pid; + try { + pid = exec(commands, environment, workingPath, in, out, err); + } catch (IOException e) { + IOException wrapper = new IOException("Error running exec()." + + " Commands: " + Arrays.toString(commands) + + " Working Directory: " + workingDirectory + + " Environment: " + Arrays.toString(environment)); + wrapper.initCause(e); + throw wrapper; + } + ProcessImpl process = new ProcessImpl(pid, in, out, err); + ProcessReference processReference + = new ProcessReference(process, referenceQueue); + processReferences.put(pid, processReference); + + /* + * This will wake up the child monitor thread in case there + * weren't previously any children to wait on. + */ + processReferences.notifyAll(); + + return process; + } + } + + static class ProcessImpl extends Process { + + /** Process ID. */ + final int id; + + final InputStream errorStream; + + /** Reads output from process. */ + final InputStream inputStream; + + /** Sends output to process. */ + final OutputStream outputStream; + + /** The process's exit value. */ + Integer exitValue = null; + final Object exitValueMutex = new Object(); + + ProcessImpl(int id, FileDescriptor in, FileDescriptor out, + FileDescriptor err) { + this.id = id; + + this.errorStream = new ProcessInputStream(err); + this.inputStream = new ProcessInputStream(in); + this.outputStream = new ProcessOutputStream(out); + } + + public void destroy() { + try { + kill(this.id); + } catch (IOException e) { + Logger.getLogger(Runtime.class.getName()).log(Level.FINE, + "Failed to destroy process " + id + ".", e); + } + } + + public int exitValue() { + synchronized (exitValueMutex) { + if (exitValue == null) { + throw new IllegalThreadStateException( + "Process has not yet terminated."); + } + + return exitValue; + } + } + + public InputStream getErrorStream() { + return this.errorStream; + } + + public InputStream getInputStream() { + return this.inputStream; + } + + public OutputStream getOutputStream() { + return this.outputStream; + } + + public int waitFor() throws InterruptedException { + synchronized (exitValueMutex) { + while (exitValue == null) { + exitValueMutex.wait(); + } + return exitValue; + } + } + + void setExitValue(int exitValue) { + synchronized (exitValueMutex) { + this.exitValue = exitValue; + exitValueMutex.notifyAll(); + } + } + + @Override + public String toString() { + return "Process[id=" + id + "]"; + } + } + + static class ProcessReference extends WeakReference<ProcessImpl> { + + final int processId; + + public ProcessReference(ProcessImpl referent, + ProcessReferenceQueue referenceQueue) { + super(referent, referenceQueue); + this.processId = referent.id; + } + } + + static class ProcessReferenceQueue extends ReferenceQueue<ProcessImpl> { + + @Override + public ProcessReference poll() { + // Why couldn't they get the generics right on ReferenceQueue? :( + Object reference = super.poll(); + return (ProcessReference) reference; + } + } + + static final ProcessManager instance = new ProcessManager(); + + /** Gets the process manager. */ + static ProcessManager getInstance() { + return instance; + } + + /** Automatically closes fd when collected. */ + private static class ProcessInputStream extends FileInputStream { + + private FileDescriptor fd; + + private ProcessInputStream(FileDescriptor fd) { + super(fd); + this.fd = fd; + } + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + synchronized (this) { + if (fd != null && fd.valid()) { + try { + ProcessManager.close(fd); + } finally { + fd = null; + } + } + } + } + } + } + + /** Automatically closes fd when collected. */ + private static class ProcessOutputStream extends FileOutputStream { + + private FileDescriptor fd; + + private ProcessOutputStream(FileDescriptor fd) { + super(fd); + this.fd = fd; + } + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + synchronized (this) { + if (fd != null && fd.valid()) { + try { + ProcessManager.close(fd); + } finally { + fd = null; + } + } + } + } + } + } + + /** Closes the given file descriptor. */ + private static native void close(FileDescriptor fd) throws IOException; +} diff --git a/luni-kernel/src/main/java/java/lang/Runtime.java b/luni-kernel/src/main/java/java/lang/Runtime.java new file mode 100644 index 0000000..28cc96f --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/Runtime.java @@ -0,0 +1,889 @@ +/* + * 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 java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.Reader; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +import java.util.StringTokenizer; +import java.util.List; +import java.util.ArrayList; + +import dalvik.system.VMDebug; +import dalvik.system.VMStack; + +/** + * Allows Java applications to interface with the environment in which they are + * running. Applications can not create an instance of this class, but they can + * get a singleton instance by invoking {@link #getRuntime()}. + * + * @see System + * + * @since Android 1.0 + */ +public class Runtime { + + /** + * Holds the Singleton global instance of Runtime. + */ + private static final Runtime mRuntime = new Runtime(); + + /** + * Holds the library paths, used for native library lookup. + */ + private final String[] mLibPaths; + + /** + * Holds the list of threads to run when the VM terminates + */ + private List<Thread> shutdownHooks = new ArrayList<Thread>(); + + /** + * Reflects whether finalization should be run for all objects + * when the VM terminates. + */ + private static boolean finalizeOnExit; + + /** + * Reflects whether we are already shutting down the VM. + */ + private boolean shuttingDown; + + /** + * Reflects whether we are tracing method calls. + */ + private boolean tracingMethods; + + /** + * Prevent this class from being instantiated. + */ + private Runtime(){ + String pathList = System.getProperty("java.library.path", "."); + String pathSep = System.getProperty("path.separator", ":"); + String fileSep = System.getProperty("file.separator", "/"); + + mLibPaths = pathList.split(pathSep); + + int i; + + if (false) + System.out.println("Runtime paths:"); + + // Add a '/' to the end so we don't have to do the property lookup + // and concatenation later. + for (i = 0; i < mLibPaths.length; i++) { + if (!mLibPaths[i].endsWith(fileSep)) + mLibPaths[i] += fileSep; + if (false) + System.out.println(" " + mLibPaths[i]); + } + } + + /** + * Executes the specified command and its arguments in a separate native + * process. The new process inherits the environment of the caller. Calling + * this method is equivalent to calling {@code exec(progArray, null, null)}. + * + * @param progArray + * the array containing the program to execute as well as any + * arguments to the program. + * @return the new {@code Process} object that represents the native + * process. + * @throws IOException + * if the requested program can not be executed. + * @throws SecurityException + * if the current {@code SecurityManager} disallows program + * execution. + * @see SecurityManager#checkExec + * @since Android 1.0 + */ + public Process exec(String[] progArray) throws java.io.IOException { + return exec(progArray, null, null); + } + + /** + * Executes the specified command and its arguments in a separate native + * process. The new process uses the environment provided in {@code envp}. + * Calling this method is equivalent to calling + * {@code exec(progArray, envp, null)}. + * + * @param progArray + * the array containing the program to execute as well as any + * arguments to the program. + * @param envp + * the array containing the environment to start the new process + * in. + * @return the new {@code Process} object that represents the native + * process. + * @throws IOException + * if the requested program can not be executed. + * @throws SecurityException + * if the current {@code SecurityManager} disallows program + * execution. + * @see SecurityManager#checkExec + * @since Android 1.0 + */ + public Process exec(String[] progArray, String[] envp) throws java.io.IOException { + return exec(progArray, envp, null); + } + + /** + * Executes the specified command and its arguments in a separate native + * process. The new process uses the environment provided in {@code envp} + * and the working directory specified by {@code directory}. + * + * @param progArray + * the array containing the program to execute as well as any + * arguments to the program. + * @param envp + * the array containing the environment to start the new process + * in. + * @param directory + * the directory in which to execute the program. If {@code null}, + * execute if in the same directory as the parent process. + * @return the new {@code Process} object that represents the native + * process. + * @throws IOException + * if the requested program can not be executed. + * @throws SecurityException + * if the current {@code SecurityManager} disallows program + * execution. + * @see SecurityManager#checkExec + * @since Android 1.0 + */ + public Process exec(String[] progArray, String[] envp, File directory) + throws java.io.IOException { + + // Sanity checks + if (progArray == null) { + throw new NullPointerException(); + } else if (progArray.length == 0) { + throw new IndexOutOfBoundsException(); + } else { + for (int i = 0; i < progArray.length; i++) { + if (progArray[i] == null) { + throw new NullPointerException(); + } + } + } + + if (envp != null) { + for (int i = 0; i < envp.length; i++) { + if (envp[i] == null) { + throw new NullPointerException(); + } + } + } + + // Security checks + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkExec(progArray[0]); + } + + // Delegate the execution + return ProcessManager.getInstance().exec(progArray, envp, directory); + } + + /** + * Executes the specified program in a separate native process. The new + * process inherits the environment of the caller. Calling this method is + * equivalent to calling {@code exec(prog, null, null)}. + * + * @param prog + * the name of the program to execute. + * @return the new {@code Process} object that represents the native + * process. + * @throws IOException + * if the requested program can not be executed. + * @throws SecurityException + * if the current {@code SecurityManager} disallows program + * execution. + * @see SecurityManager#checkExec + * @since Android 1.0 + */ + public Process exec(String prog) throws java.io.IOException { + return exec(prog, null, null); + } + + /** + * Executes the specified program in a separate native process. The new + * process uses the environment provided in {@code envp}. Calling this + * method is equivalent to calling {@code exec(prog, envp, null)}. + * + * @param prog + * the name of the program to execute. + * @param envp + * the array containing the environment to start the new process + * in. + * @return the new {@code Process} object that represents the native + * process. + * @throws IOException + * if the requested program can not be executed. + * @throws SecurityException + * if the current {@code SecurityManager} disallows program + * execution. + * @see SecurityManager#checkExec + * @since Android 1.0 + */ + public Process exec(String prog, String[] envp) throws java.io.IOException { + return exec(prog, envp, null); + } + + /** + * Executes the specified program in a separate native process. The new + * process uses the environment provided in {@code envp} and the working + * directory specified by {@code directory}. + * + * @param prog + * the name of the program to execute. + * @param envp + * the array containing the environment to start the new process + * in. + * @param directory + * the directory in which to execute the program. If {@code null}, + * execute if in the same directory as the parent process. + * @return the new {@code Process} object that represents the native + * process. + * @throws IOException + * if the requested program can not be executed. + * @throws SecurityException + * if the current {@code SecurityManager} disallows program + * execution. + * @see SecurityManager#checkExec + * @since Android 1.0 + */ + public Process exec(String prog, String[] envp, File directory) throws java.io.IOException { + // Sanity checks + if (prog == null) { + throw new NullPointerException(); + } else if (prog.length() == 0) { + throw new IllegalArgumentException(); + } + + // Break down into tokens, as described in Java docs + StringTokenizer tokenizer = new StringTokenizer(prog); + int length = tokenizer.countTokens(); + String[] progArray = new String[length]; + for (int i = 0; i < length; i++) { + progArray[i] = tokenizer.nextToken(); + } + + // Delegate + return exec(progArray, envp, directory); + } + + /** + * Causes the virtual machine to stop running and the program to exit. If + * {@link #runFinalizersOnExit(boolean)} has been previously invoked with a + * {@code true} argument, then all all objects will be properly + * garbage-collected and finalized first. + * + * @param code + * the return code. By convention, non-zero return codes indicate + * abnormal terminations. + * @throws SecurityException + * if the current {@code SecurityManager} does not allow the + * running thread to terminate the virtual machine. + * @see SecurityManager#checkExit + * @since Android 1.0 + */ + public void exit(int code) { + // Security checks + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkExit(code); + } + + // Make sure we don't try this several times + synchronized(this) { + if (!shuttingDown) { + shuttingDown = true; + + Thread[] hooks; + synchronized (shutdownHooks) { + // create a copy of the hooks + hooks = new Thread[shutdownHooks.size()]; + shutdownHooks.toArray(hooks); + } + + // Start all shutdown hooks concurrently + for (int i = 0; i < hooks.length; i++) { + hooks[i].start(); + } + + // Wait for all shutdown hooks to finish + for (Thread hook : hooks) { + try { + hook.join(); + } catch (InterruptedException ex) { + // Ignore, since we are at VM shutdown. + } + } + + // Ensure finalization on exit, if requested + if (finalizeOnExit) { + runFinalization(true); + } + + // Get out of here finally... + nativeExit(code, true); + } + } + } + + /** + * Returns the amount of free memory resources which are available to the + * running program. + * + * @return the approximate amount of free memory, measured in bytes. + * @since Android 1.0 + */ + public native long freeMemory(); + + /** + * Indicates to the virtual machine that it would be a good time to run the + * garbage collector. Note that this is a hint only. There is no guarantee + * that the garbage collector will actually be run. + * + * @since Android 1.0 + */ + public native void gc(); + + /** + * Returns the single {@code Runtime} instance. + * + * @return the {@code Runtime} object for the current application. + * @since Android 1.0 + */ + public static Runtime getRuntime() { + return mRuntime; + } + + /** + * Loads and links the dynamic library that is identified through the + * specified path. This method is similar to {@link #loadLibrary(String)}, + * but it accepts a full path specification whereas {@code loadLibrary} just + * accepts the name of the library to load. + * + * @param pathName + * the absolute (platform dependent) path to the library to load. + * @throws UnsatisfiedLinkError + * if the library can not be loaded. + * @throws SecurityException + * if the current {@code SecurityManager} does not allow to load + * the library. + * @see SecurityManager#checkLink + * @since Android 1.0 + */ + public void load(String pathName) { + // Security checks + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkLink(pathName); + } + + load(pathName, VMStack.getCallingClassLoader()); + } + + /* + * Loads and links a library without security checks. + */ + void load(String filename, ClassLoader loader) { + if (filename == null) { + throw new NullPointerException("library path was null."); + } + if (!nativeLoad(filename, loader)) { + throw new UnsatisfiedLinkError( + "Library " + filename + " not found"); + } + } + + /** + * Loads and links the library with the specified name. The mapping of the + * specified library name to the full path for loading the library is + * implementation-dependent. + * + * @param libName + * the name of the library to load. + * @throws UnsatisfiedLinkError + * if the library can not be loaded. + * @throws SecurityException + * if the current {@code SecurityManager} does not allow to load + * the library. + * @see SecurityManager#checkLink + * @since Android 1.0 + */ + public void loadLibrary(String libName) { + // Security checks + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkLink(libName); + } + + loadLibrary(libName, VMStack.getCallingClassLoader()); + } + + /* + * Loads and links a library without security checks. + */ + void loadLibrary(String libname, ClassLoader loader) { + String filename; + int i; + + if (loader != null) { + filename = loader.findLibrary(libname); + if (filename != null && nativeLoad(filename, loader)) + return; + // else fall through to exception + } else { + filename = System.mapLibraryName(libname); + for (i = 0; i < mLibPaths.length; i++) { + if (false) + System.out.println("Trying " + mLibPaths[i] + filename); + if (nativeLoad(mLibPaths[i] + filename, loader)) + return; + } + } + + throw new UnsatisfiedLinkError("Library " + libname + " not found"); + } + + private static native void nativeExit(int code, boolean isExit); + + private static native boolean nativeLoad(String filename, + ClassLoader loader); + + /** + * Requests proper finalization for all Objects on the heap. + * + * @param forced Decides whether the VM really needs to do this (true) + * or if this is just a suggestion that can safely be ignored + * (false). + */ + private native void runFinalization(boolean forced); + + /** + * Provides a hint to the virtual machine that it would be useful to attempt + * to perform any outstanding object finalizations. + * + * @since Android 1.0 + */ + public void runFinalization() { + runFinalization(false); + } + + /** + * Sets the flag that indicates whether all objects are finalized when the + * virtual machine is about to exit. Note that all finalization which occurs + * when the system is exiting is performed after all running threads have + * been terminated. + * + * @param run + * {@code true} to enable finalization on exit, {@code false} to + * disable it. + * @deprecated This method is unsafe. + * @since Android 1.0 + */ + @Deprecated + public static void runFinalizersOnExit(boolean run) { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkExit(0); + } + finalizeOnExit = run; + } + + /** + * Returns the total amount of memory which is available to the running + * program. + * + * @return the total amount of memory, measured in bytes. + * @since Android 1.0 + */ + public native long totalMemory(); + + /** + * Switches the output of debug information for instructions on or off. + * For the Android 1.0 reference implementation, this method does nothing. + * + * @param enable + * {@code true} to switch tracing on, {@code false} to switch it + * off. + * @since Android 1.0 + */ + public void traceInstructions(boolean enable) { + // TODO(Google) Provide some implementation for this. + return; + } + + /** + * Switches the output of debug information for methods on or off. + * + * @param enable + * {@code true} to switch tracing on, {@code false} to switch it + * off. + * @since Android 1.0 + */ + public void traceMethodCalls(boolean enable) { + if (enable != tracingMethods) { + if (enable) { + VMDebug.startMethodTracing(); + } else { + VMDebug.stopMethodTracing(); + } + tracingMethods = enable; + } + } + + /** + * Returns the localized version of the specified input stream. The input + * stream that is returned automatically converts all characters from the + * local character set to Unicode after reading them from the underlying + * stream. + * + * @param stream + * the input stream to localize. + * @return the localized input stream. + * @deprecated Use {@link InputStreamReader}. + * @since Android 1.0 + */ + @Deprecated + public InputStream getLocalizedInputStream(InputStream stream) { + if (System.getProperty("file.encoding", "UTF-8").equals("UTF-8")) { + return stream; + } + return new ReaderInputStream(stream); + } + + /** + * Returns the localized version of the specified output stream. The output + * stream that is returned automatically converts all characters from + * Unicode to the local character set before writing them to the underlying + * stream. + * + * @param stream + * the output stream to localize. + * @return the localized output stream. + * @deprecated Use {@link OutputStreamWriter}. + * @since Android 1.0 + */ + @Deprecated + public OutputStream getLocalizedOutputStream(OutputStream stream) { + if (System.getProperty("file.encoding", "UTF-8").equals("UTF-8")) { + return stream; + } + return new WriterOutputStream(stream ); + } + + /** + * Registers a virtual-machine shutdown hook. A shutdown hook is a + * {@code Thread} that is ready to run, but has not yet been started. All + * registered shutdown hooks will be executed once the virtual machine shuts + * down properly. A proper shutdown happens when either the + * {@link #exit(int)} method is called or the surrounding system decides to + * terminate the application, for example in response to a {@code CTRL-C} or + * a system-wide shutdown. A termination of the virtual machine due to the + * {@link #halt(int)} method, an {@link Error} or a {@code SIGKILL}, in + * contrast, is not considered a proper shutdown. In these cases the + * shutdown hooks will not be run. + * <p> + * Shutdown hooks are run concurrently and in an unspecified order. Hooks + * failing due to an unhandled exception are not a problem, but the stack + * trace might be printed to the console. Once initiated, the whole shutdown + * process can only be terminated by calling {@code halt()}. + * <p> + * If {@link #runFinalizersOnExit(boolean)} has been called with a {@code + * true} argument, garbage collection and finalization will take place after + * all hooks are either finished or have failed. Then the virtual machine + * terminates. + * <p> + * It is recommended that shutdown hooks do not do any time-consuming + * activities, in order to not hold up the shutdown process longer than + * necessary. + * + * @param hook + * the shutdown hook to register. + * @throws IllegalArgumentException + * if the hook has already been started or if it has already + * been registered. + * @throws IllegalStateException + * if the virtual machine is already shutting down. + * @throws SecurityException + * if a SecurityManager is registered and the calling code + * doesn't have the RuntimePermission("shutdownHooks"). + */ + public void addShutdownHook(Thread hook) { + // Sanity checks + if (hook == null) { + throw new NullPointerException("Hook may not be null."); + } + + if (shuttingDown) { + throw new IllegalStateException("VM already shutting down"); + } + + if (hook.hasBeenStarted) { + throw new IllegalArgumentException("Hook has already been started"); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("shutdownHooks")); + } + + synchronized (shutdownHooks) { + if (shutdownHooks.contains(hook)) { + throw new IllegalArgumentException("Hook already registered."); + } + + shutdownHooks.add(hook); + } + } + + /** + * Unregisters a previously registered virtual machine shutdown hook. + * + * @param hook + * the shutdown hook to remove. + * @return {@code true} if the hook has been removed successfully; {@code + * false} otherwise. + * @throws IllegalStateException + * if the virtual machine is already shutting down. + * @throws SecurityException + * if a SecurityManager is registered and the calling code + * doesn't have the RuntimePermission("shutdownHooks"). + */ + public boolean removeShutdownHook(Thread hook) { + // Sanity checks + if (hook == null) { + throw new NullPointerException("Hook may not be null."); + } + + if (shuttingDown) { + throw new IllegalStateException("VM already shutting down"); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("shutdownHooks")); + } + + synchronized (shutdownHooks) { + return shutdownHooks.remove(hook); + } + } + + /** + * Causes the virtual machine to stop running, and the program to exit. + * Neither shutdown hooks nor finalizers are run before. + * + * @param code + * the return code. By convention, non-zero return codes indicate + * abnormal terminations. + * @throws SecurityException + * if the current {@code SecurityManager} does not allow the + * running thread to terminate the virtual machine. + * @see SecurityManager#checkExit + * @see #addShutdownHook(Thread) + * @see #removeShutdownHook(Thread) + * @see #runFinalizersOnExit(boolean) + * @since Android 1.0 + */ + public void halt(int code) { + // Security checks + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkExit(code); + } + + // Get out of here... + nativeExit(code, false); + } + + /** + * Returns the number of processors available to the virtual machine. The + * Android reference implementation (currently) always returns 1. + * + * @return the number of available processors, at least 1. + * @since Android 1.0 + */ + public int availableProcessors() { + return 1; + } + + /** + * Returns the maximum amount of memory that may be used by the virtual + * machine, or {@code Long.MAX_VALUE} if there is no such limit. + * + * @return the maximum amount of memory that the virtual machine will try to + * allocate, measured in bytes. + * @since Android 1.0 + */ + public native long maxMemory(); + +} + +/* + * Internal helper class for creating a localized InputStream. A reader + * wrapped in an InputStream. + */ +class ReaderInputStream extends InputStream { + + private Reader reader; + + private Writer writer; + + ByteArrayOutputStream out = new ByteArrayOutputStream(256); + + private byte[] bytes; + + private int nextByte; + + private int numBytes; + + String encoding = System.getProperty("file.encoding", "UTF-8"); + + public ReaderInputStream(InputStream stream) { + try { + reader = new InputStreamReader(stream, "UTF-8"); + writer = new OutputStreamWriter(out, encoding); + } catch (UnsupportedEncodingException e) { + // Should never happen, since UTF-8 and platform encoding must be + // supported. + throw new RuntimeException(e); + } + } + + @Override + public int read() throws IOException { + if (nextByte >= numBytes) { + readBuffer(); + } + + return (numBytes < 0) ? -1 : bytes[nextByte++]; + } + + private void readBuffer() throws IOException { + char[] chars = new char[128]; + int read = reader.read(chars); + if (read < 0) { + numBytes = read; + return; + } + + writer.write(chars, 0, read); + writer.flush(); + bytes = out.toByteArray(); + numBytes = bytes.length; + nextByte = 0; + } + +} + +/* + * Internal helper class for creating a localized OutputStream. A writer + * wrapped in an OutputStream. Bytes are written to characters in big-endian + * fashion. + */ +class WriterOutputStream extends OutputStream { + + private Reader reader; + + private Writer writer; + + private PipedOutputStream out; + + private PipedInputStream pipe; + + private int numBytes; + + private String enc = System.getProperty("file.encoding", "UTF-8"); + + public WriterOutputStream(OutputStream stream) { + try { + // sink + this.writer = new OutputStreamWriter(stream, enc); + + // transcriber + out = new PipedOutputStream(); + pipe = new PipedInputStream(out); + this.reader = new InputStreamReader(pipe, "UTF-8"); + + } catch (UnsupportedEncodingException e) { + // Should never happen, since platform encoding must be supported. + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void write(int b) throws IOException { + out.write(b); + if( ++numBytes > 256) { + flush(); + numBytes = 0; + } + } + + @Override + public void flush() throws IOException { + out.flush(); + char[] chars = new char[128]; + if (pipe.available() > 0) { + int read = reader.read(chars); + if (read > 0) { + writer.write(chars, 0, read); + } + } + writer.flush(); + } + + @Override + public void close() throws IOException { + out.close(); + flush(); + writer.close(); + } +} diff --git a/luni-kernel/src/main/java/java/lang/StackTraceElement.java b/luni-kernel/src/main/java/java/lang/StackTraceElement.java new file mode 100644 index 0000000..5394ae9 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/StackTraceElement.java @@ -0,0 +1,245 @@ +/* + * 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.io.Serializable; + +/** + * A representation of a single stack frame. Arrays of {@code StackTraceElement} + * are stored in {@link Throwable} objects to represent the whole state of the + * call stack at the time a {@code Throwable} gets thrown. + * + * @see Throwable#getStackTrace() + * @since Android 1.0 + */ +public final class StackTraceElement implements Serializable { + + private static final long serialVersionUID = 6992337162326171013L; + + // BEGIN android-added + private static final int NATIVE_LINE_NUMBER = -2; + // END android-added + + String declaringClass; + + String methodName; + + String fileName; + + int lineNumber; + + /** + * Constructs a new {@code StackTraceElement} for a specified execution + * point. + * + * @param cls + * the fully qualified name of the class where execution is at. + * @param method + * the name of the method where execution is at. + * @param file + * The name of the file where execution is at or {@code null}. + * @param line + * the line of the file where execution is at, a negative number + * if unknown or {@code -2} if the execution is in a native + * method. + * @throws NullPointerException + * if {@code cls} or {@code method} is {@code null}. + * @since Android 1.0 + */ + public StackTraceElement(String cls, String method, String file, int line) { + super(); + if (cls == null || method == null) { + throw new NullPointerException(); + } + declaringClass = cls; + methodName = method; + fileName = file; + lineNumber = line; + } + + /** + * <p> + * Private, nullary constructor for VM use only. + * </p> + */ + private StackTraceElement() { + super(); + } + + /** + * Compares this instance with the specified object and indicates if they + * are equal. In order to be equal, the following conditions must be + * fulfilled: + * <ul> + * <li>{@code obj} must be a stack trace element,</li> + * <li>the method names of this stack trace element and of {@code obj} must + * not be {@code null},</li> + * <li>the class, method and file names as well as the line number of this + * stack trace element and of {@code obj} must be equal.</li> + * </ul> + * + * @param obj + * the object to compare this instance with. + * @return {@code true} if the specified object is equal to this + * {@code StackTraceElement}; {@code false} otherwise. + * @see #hashCode + * @since Android 1.0 + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof StackTraceElement)) { + return false; + } + StackTraceElement castObj = (StackTraceElement) obj; + + /* + * Unknown methods are never equal to anything (not strictly to spec, + * but spec does not allow null method/class names) + */ + if ((methodName == null) || (castObj.methodName == null)) { + return false; + } + + if (!getMethodName().equals(castObj.getMethodName())) { + return false; + } + if (!getClassName().equals(castObj.getClassName())) { + return false; + } + String localFileName = getFileName(); + if (localFileName == null) { + if (castObj.getFileName() != null) { + return false; + } + } else { + if (!localFileName.equals(castObj.getFileName())) { + return false; + } + } + if (getLineNumber() != castObj.getLineNumber()) { + return false; + } + + return true; + } + + /** + * Returns the fully qualified name of the class belonging to this + * {@code StackTraceElement}. + * + * @return the fully qualified type name of the class + * @since Android 1.0 + */ + public String getClassName() { + return (declaringClass == null) ? "<unknown class>" : declaringClass; + } + + /** + * Returns the name of the Java source file containing class belonging to + * this {@code StackTraceElement}. + * + * @return the name of the file, or {@code null} if this information is not + * available. + * @since Android 1.0 + */ + public String getFileName() { + return fileName; + } + + /** + * Returns the line number in the source for the class belonging to this + * {@code StackTraceElement}. + * + * @return the line number, or a negative number if this information is not + * available. + * @since Android 1.0 + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * Returns the name of the method belonging to this {@code + * StackTraceElement}. + * + * @return the name of the method, or "<unknown method>" if this information + * is not available. + * @since Android 1.0 + */ + public String getMethodName() { + return (methodName == null) ? "<unknown method>" : methodName; + } + + @Override + public int hashCode() { + /* + * Either both methodName and declaringClass are null, or neither are + * null. + */ + if (methodName == null) { + // all unknown methods hash the same + return 0; + } + // declaringClass never null if methodName is non-null + return methodName.hashCode() ^ declaringClass.hashCode(); + } + + /** + * Indicates if the method name returned by {@link #getMethodName()} is + * implemented as a native method. + * + * @return {@code true} if the method in which this stack trace element is + * executing is a native method; {@code false} otherwise. + * @since Android 1.0 + */ + public boolean isNativeMethod() { + // BEGIN android-changed + return lineNumber == NATIVE_LINE_NUMBER; + // END android-changed + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(80); + + buf.append(getClassName()); + buf.append('.'); + buf.append(getMethodName()); + + if (isNativeMethod()) { + buf.append("(Native Method)"); + } else { + String fName = getFileName(); + + if (fName == null) { + buf.append("(Unknown Source)"); + } else { + int lineNum = getLineNumber(); + + buf.append('('); + buf.append(fName); + if (lineNum >= 0) { + buf.append(':'); + buf.append(lineNum); + } + buf.append(')'); + } + } + return buf.toString(); + } +} diff --git a/luni-kernel/src/main/java/java/lang/System.java b/luni-kernel/src/main/java/java/lang/System.java new file mode 100644 index 0000000..b97a75a --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/System.java @@ -0,0 +1,755 @@ +/* + * 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 java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.channels.Channel; +import java.nio.channels.spi.SelectorProvider; +import java.security.SecurityPermission; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.PropertyPermission; +import java.util.Set; + +import dalvik.system.VMStack; + +/** + * Provides access to system-related information and resources including + * standard input and output. Enables clients to dynamically load native + * libraries. All methods of this class are accessed in a static way and the + * class itself can not be instantiated. + * + * @see Runtime + * + * @since Android 1.0 + */ +public final class System { + + /** + * Default input stream. + * + * @since Android 1.0 + */ + public static final InputStream in; + + /** + * Default output stream. + * + * @since Android 1.0 + */ + public static final PrintStream out; + + /** + * Default error output stream. + * + * @since Android 1.0 + */ + public static final PrintStream err; + + /** + * The System Properties table. + */ + private static Properties systemProperties; + + /** + * The System default SecurityManager. + */ + private static SecurityManager securityManager; + + /** + * Initialize all the slots in System on first use. + */ + static { + /* + * Set up standard in, out, and err. TODO err and out are + * String.ConsolePrintStream. All three are buffered in Harmony. Check + * and possibly change this later. + */ + err = new PrintStream(new FileOutputStream(FileDescriptor.err)); + out = new PrintStream(new FileOutputStream(FileDescriptor.out)); + in = new FileInputStream(FileDescriptor.in); + } + + /** + * Sets the standard input stream to the given user defined input stream. + * + * @param newIn + * the user defined input stream to set as the standard input + * stream. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPermission()} method does not allow the change of the + * stream. + * @since Android 1.0 + */ + public static void setIn(InputStream newIn) { + SecurityManager secMgr = System.getSecurityManager(); + if(secMgr != null) { + secMgr.checkPermission(RuntimePermission.permissionToSetIO); + } + setFieldImpl("in", "Ljava/io/InputStream;", newIn); + } + + /** + * Sets the standard output stream to the given user defined output stream. + * + * @param newOut + * the user defined output stream to set as the standard output + * stream. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPermission()} method does not allow the change of the + * stream. + * @since Android 1.0 + */ + public static void setOut(java.io.PrintStream newOut) { + SecurityManager secMgr = System.getSecurityManager(); + if(secMgr != null) { + secMgr.checkPermission(RuntimePermission.permissionToSetIO); + } + setFieldImpl("out", "Ljava/io/PrintStream;", newOut); + } + + /** + * Sets the standard error output stream to the given user defined output + * stream. + * + * @param newErr + * the user defined output stream to set as the standard error + * output stream. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPermission()} method does not allow the change of the + * stream. + * @since Android 1.0 + */ + public static void setErr(java.io.PrintStream newErr) { + SecurityManager secMgr = System.getSecurityManager(); + if(secMgr != null) { + secMgr.checkPermission(RuntimePermission.permissionToSetIO); + } + setFieldImpl("err", "Ljava/io/PrintStream;", newErr); + } + + /** + * Prevents this class from being instantiated. + */ + private System() { + } + + /** + * Copies the number of {@code length} elements of the Array {@code src} + * starting at the offset {@code srcPos} into the Array {@code dest} at + * the position {@code destPos}. + * + * @param src + * the source array to copy the content. + * @param srcPos + * the starting index of the content in {@code src}. + * @param dest + * the destination array to copy the data into. + * @param destPos + * the starting index for the copied content in {@code dest}. + * @param length + * the number of elements of the {@code array1} content they have + * to be copied. + * @since Android 1.0 + */ + public static native void arraycopy(Object src, int srcPos, Object dest, + int destPos, int length); + + /** + * Returns the current system time in milliseconds since January 1, 1970 + * 00:00:00 UTC. This method shouldn't be used for measuring timeouts or + * other elapsed time measurements, as changing the system time can affect + * the results. + * + * @return the local system time in milliseconds. + * @since Android 1.0 + */ + public static native long currentTimeMillis(); + + /** + * Returns the current timestamp of the most precise timer available on the + * local system. This timestamp can only be used to measure an elapsed + * period by comparing it against another timestamp. It cannot be used as a + * very exact system time expression. + * + * @return the current timestamp in nanoseconds. + * @since Android 1.0 + */ + public static native long nanoTime(); + + /** + * Causes the virtual machine to stop running and the program to exit. If + * {@link #runFinalizersOnExit(boolean)} has been previously invoked with a + * {@code true} argument, then all all objects will be properly + * garbage-collected and finalized first. + * + * @param code + * the return code. + * @throws SecurityException + * if the running thread has not enough permission to exit the + * virtual machine. + * @see SecurityManager#checkExit + * @since Android 1.0 + */ + public static void exit(int code) { + Runtime.getRuntime().exit(code); + } + + /** + * Indicates to the virtual machine that it would be a good time to run the + * garbage collector. Note that this is a hint only. There is no guarantee + * that the garbage collector will actually be run. + * + * @since Android 1.0 + */ + public static void gc() { + Runtime.getRuntime().gc(); + } + + /** + * Returns the value of the environment variable with the given name {@code + * var}. + * + * @param name + * the name of the environment variable. + * @return the value of the specified environment variable or {@code null} + * if no variable exists with the given name. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPermission()} method does not allow the querying of + * single environment variables. + * + * @since Android 1.0 + */ + public static String getenv(String name) { + if (name == null) { + throw new NullPointerException(); + } + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + secMgr.checkPermission(new RuntimePermission("getenv." + name)); + } + + return getEnvByName(name); + } + + /* + * Returns an environment variable. No security checks are performed. + * @param var the name of the environment variable + * @return the value of the specified environment variable + */ + private static native String getEnvByName(String name); + + /** + * Returns an unmodifiable map of all available environment variables. + * + * @return the map representing all environment variables. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPermission()} method does not allow the querying of + * all environment variables. + * @since Android 1.0 + */ + public static Map<String, String> getenv() { + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + secMgr.checkPermission(new RuntimePermission("getenv.*")); + } + + Map<String, String> map = new HashMap<String, String>(); + + int index = 0; + String entry = getEnvByIndex(index++); + while (entry != null) { + int pos = entry.indexOf('='); + if (pos != -1) { + map.put(entry.substring(0, pos), entry.substring(pos + 1)); + } + + entry = getEnvByIndex(index++); + } + + return new SystemEnvironment(map); + } + + /* + * Returns an environment variable. No security checks are performed. The + * safe way of traversing the environment is to start at index zero and + * count upwards until a null pointer is encountered. This marks the end of + * the Unix environment. + * @param index the index of the environment variable + * @return the value of the specified environment variable + */ + private static native String getEnvByIndex(int index); + + /** + * Returns the inherited channel from the creator of the current virtual + * machine. + * + * @return the inherited {@link Channel} or {@code null} if none exists. + * @throws IOException + * if an I/O error occurred. + * @see SelectorProvider + * @see SelectorProvider#inheritedChannel() + * @since Android 1.0 + */ + public static Channel inheritedChannel() throws IOException { + return SelectorProvider.provider().inheritedChannel(); + } + + /** + * Returns the system properties. Note that this is not a copy, so that + * changes made to the returned Properties object will be reflected in + * subsequent calls to getProperty and getProperties. + * + * @return the system properties. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPropertiesAccess()} method does not allow the operation. + * @since Android 1.0 + */ + public static Properties getProperties() { + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + secMgr.checkPropertiesAccess(); + } + + return internalGetProperties(); + } + + /** + * Returns the system properties without any security checks. This is used + * for access from within java.lang. + * + * @return the system properties + */ + static Properties internalGetProperties() { + if (System.systemProperties == null) { + SystemProperties props = new SystemProperties(); + props.preInit(); + props.postInit(); + System.systemProperties = props; + } + + return systemProperties; + } + + /** + * Returns the value of a particular system property or {@code null} if no + * such property exists. + * <p> + * The properties currently provided by the virtual machine are: + * + * <pre> + * java.vendor.url + * java.class.path + * user.home + * java.class.version + * os.version + * java.vendor + * user.dir + * user.timezone + * path.separator + * os.name + * os.arch + * line.separator + * file.separator + * user.name + * java.version + * java.home + * </pre> + * + * @param prop + * the name of the system property to look up. + * @return the value of the specified system property or {@code null} if the + * property doesn't exist. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPropertyAccess()} method does not allow the operation. + * @since Android 1.0 + */ + public static String getProperty(String prop) { + return getProperty(prop, null); + } + + /** + * Returns the value of a particular system property. The {@code + * defaultValue} will be returned if no such property has been found. + * + * @param prop + * the name of the system property to look up. + * @param defaultValue + * the return value if the system property with the given name + * does not exist. + * @return the value of the specified system property or the {@code + * defaultValue} if the property does not exist. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPropertyAccess()} method does not allow the operation. + * @since Android 1.0 + */ + public static String getProperty(String prop, String defaultValue) { + if (prop.length() == 0) { + throw new IllegalArgumentException(); + } + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + secMgr.checkPropertyAccess(prop); + } + + return internalGetProperties().getProperty(prop, defaultValue); + } + + /** + * Sets the value of a particular system property. + * + * @param prop + * the name of the system property to be changed. + * @param value + * the value to associate with the given property {@code prop}. + * @return the old value of the property or {@code null} if the property + * didn't exist. + * @throws SecurityException + * if a security manager exists and write access to the + * specified property is not allowed. + * @since Android 1.0 + */ + public static String setProperty(String prop, String value) { + if (prop.length() == 0) { + throw new IllegalArgumentException(); + } + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + secMgr.checkPermission(new PropertyPermission(prop, "write")); + } + return (String)internalGetProperties().setProperty(prop, value); + } + + /** + * Removes a specific system property. + * + * @param key + * the name of the system property to be removed. + * @return the property value or {@code null} if the property didn't exist. + * @throws NullPointerException + * if the argument {@code key} is {@code null}. + * @throws IllegalArgumentException + * if the argument {@code key} is empty. + * @throws SecurityException + * if a security manager exists and write access to the + * specified property is not allowed. + * @since Android 1.0 + */ + public static String clearProperty(String key) { + if (key == null) { + throw new NullPointerException(); + } + if (key.length() == 0) { + throw new IllegalArgumentException(); + } + + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + secMgr.checkPermission(new PropertyPermission(key, "write")); + } + return (String)internalGetProperties().remove(key); + } + + /** + * Returns the active security manager. + * + * @return the system security manager object. + * @since Android 1.0 + */ + public static SecurityManager getSecurityManager() { + return securityManager; + } + + /** + * Returns an integer hash code for the parameter. The hash code returned is + * the same one that would be returned by the method {@code + * java.lang.Object.hashCode()}, whether or not the object's class has + * overridden hashCode(). The hash code for {@code null} is {@code 0}. + * + * @param anObject + * the object to calculate the hash code. + * @return the hash code for the given object. + * @see java.lang.Object#hashCode + * @since Android 1.0 + */ + public static native int identityHashCode(Object anObject); + + /** + * Loads the specified file as a dynamic library. + * + * @param pathName + * the path of the file to be loaded. + * @throws SecurityException + * if the library was not allowed to be loaded. + * @since Android 1.0 + */ + public static void load(String pathName) { + SecurityManager smngr = System.getSecurityManager(); + if (smngr != null) { + smngr.checkLink(pathName); + } + Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader()); + } + + /** + * Loads and links the shared library with the given name {@code libName}. + * The file will be searched in the default directory for shared libraries + * of the local system. + * + * @param libName + * the name of the library to load. + * @throws UnsatisfiedLinkError + * if the library could not be loaded. + * @throws SecurityException + * if the library was not allowed to be loaded. + * @since Android 1.0 + */ + public static void loadLibrary(String libName) { + SecurityManager smngr = System.getSecurityManager(); + if (smngr != null) { + smngr.checkLink(libName); + } + Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader()); + } + + /** + * Provides a hint to the virtual machine that it would be useful to attempt + * to perform any outstanding object finalizations. + * + * @since Android 1.0 + */ + public static void runFinalization() { + Runtime.getRuntime().runFinalization(); + } + + /** + * Ensures that, when the virtual machine is about to exit, all objects are + * finalized. Note that all finalization which occurs when the system is + * exiting is performed after all running threads have been terminated. + * + * @param flag + * the flag determines if finalization on exit is enabled. + * @deprecated this method is unsafe. + * @since Android 1.0 + */ + @SuppressWarnings("deprecation") + @Deprecated + public static void runFinalizersOnExit(boolean flag) { + Runtime.runFinalizersOnExit(flag); + } + + /** + * Sets all system properties. + * + * @param p + * the new system property. + * @throws SecurityException + * if a {@link SecurityManager} is installed and its {@code + * checkPropertiesAccess()} method does not allow the operation. + * @since Android 1.0 + */ + public static void setProperties(Properties p) { + SecurityManager secMgr = System.getSecurityManager(); + if (secMgr != null) { + secMgr.checkPropertiesAccess(); + } + + systemProperties = p; + } + + /** + * Sets the active security manager. Note that once the security manager has + * been set, it can not be changed. Attempts to do that will cause a + * security exception. + * + * @param sm + * the new security manager. + * @throws SecurityException + * if the security manager has already been set and if its + * checkPermission method does not allow to redefine the + * security manager. + * @since Android 1.0 + */ + public static void setSecurityManager(final SecurityManager sm) { + if (securityManager != null) { + securityManager.checkPermission(new java.lang.RuntimePermission("setSecurityManager")); + } + + if (sm != null) { + // before the new manager assumed office, make a pass through + // the common operations and let it load needed classes (if any), + // to avoid infinite recursion later on + try { + sm.checkPermission(new SecurityPermission("getProperty.package.access")); + } catch (Exception ignore) { + } + try { + sm.checkPackageAccess("java.lang"); + } catch (Exception ignore) { + } + } + + securityManager = sm; + } + + /** + * Returns the platform specific file name format for the shared library + * named by the argument. + * + * @param userLibName + * the name of the library to look up. + * @return the platform specific filename for the library. + * @since Android 1.0 + */ + public static native String mapLibraryName(String userLibName); + + /** + * Sets the value of the named static field in the receiver to the passed in + * argument. + * + * @param fieldName + * the name of the field to set, one of in, out, or err + * @param stream + * the new value of the field + */ + private static native void setFieldImpl(String fieldName, String signature, Object stream); + +} + +/** + * Internal class holding the System properties. Needed by the Dalvik VM for the + * two native methods. Must not be a local class, since we don't have a System + * instance. + */ +class SystemProperties extends Properties { + // Dummy, just to make the compiler happy. + + native void preInit(); + + native void postInit(); +} + +/** + * Internal class holding the System environment variables. The Java spec + * mandates that this map be read-only, so we wrap our real map into this one + * and make sure no one touches the contents. We also check for null parameters + * and do some (seemingly unnecessary) type casts to fulfill the contract layed + * out in the spec. + */ +class SystemEnvironment implements Map { + + private Map<String, String> map; + + public SystemEnvironment(Map<String, String> map) { + this.map = map; + } + + public void clear() { + throw new UnsupportedOperationException("Can't modify environment"); + } + + @SuppressWarnings("cast") + public boolean containsKey(Object key) { + if (key == null) { + throw new NullPointerException(); + } + + return map.containsKey((String)key); + } + + @SuppressWarnings("cast") + public boolean containsValue(Object value) { + if (value == null) { + throw new NullPointerException(); + } + + return map.containsValue((String)value); + } + + public Set entrySet() { + return map.entrySet(); + } + + @SuppressWarnings("cast") + public String get(Object key) { + if (key == null) { + throw new NullPointerException(); + } + + return map.get((String)key); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public Set<String> keySet() { + return map.keySet(); + } + + public String put(Object key, Object value) { + throw new UnsupportedOperationException("Can't modify environment"); + } + + public void putAll(Map map) { + throw new UnsupportedOperationException("Can't modify environment"); + } + + public String remove(Object key) { + throw new UnsupportedOperationException("Can't modify environment"); + } + + public int size() { + return map.size(); + } + + public Collection values() { + return map.values(); + } + +} diff --git a/luni-kernel/src/main/java/java/lang/Thread.java b/luni-kernel/src/main/java/java/lang/Thread.java new file mode 100644 index 0000000..d3795f8 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/Thread.java @@ -0,0 +1,1559 @@ +/* + * 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.security.AccessController; +import java.util.Map; +import java.util.HashMap; + +import org.apache.harmony.security.fortress.SecurityUtils; + +/** + * 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 virtual + * machine instance has at least one main {@code Thread} running when it is + * started; typically, there are several others for housekeeping. The + * application might decide to launch additional {@code Thread}s for specific + * purposes. + * <p> + * {@code Thread}s in the same VM interact and synchronize by the use of shared + * objects and monitors associated with these objects. Synchronized methods and + * part of the API in {@link Object} also allow {@code Thread}s to cooperate. + * <p> + * There are basically two main ways of having a {@code Thread} execute + * application code. One is providing a new class that extends {@code Thread} + * and overriding its {@link #run()} method. The other is providing a new + * {@code Thread} instance with a {@link Runnable} object during its creation. + * In both cases, the {@link #start()} method must be called to actually execute + * the new {@code Thread}. + * <p> + * Each {@code Thread} has an integer priority that basically determines the + * amount of CPU time the {@code Thread} gets. It can be set using the + * {@link #setPriority(int)} method. A {@code Thread} can also be made a daemon, + * which makes it run in the background. The latter also affects VM termination + * behavior: the VM does not terminate automatically as long as there are + * non-daemon threads running. + * + * @see java.lang.Object + * @see java.lang.ThreadGroup + * + * @since Android 1.0 + */ +public class Thread implements Runnable { + + /** 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. + * + * @since Android 1.0 + */ + 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. + * + * @since Android 1.0 + */ + public final static int MAX_PRIORITY = 10; + + /** + * The minimum priority value allowed for a thread. + * + * @since Android 1.0 + */ + public final static int MIN_PRIORITY = 1; + + /** + * The normal (default) priority value assigned to threads. + * + * @since Android 1.0 + */ + public final static 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; + + /** + * Holds the interrupt action for this Thread, if any. + * <p> + * This is required internally by NIO, so even if it looks like it's + * useless, don't delete it! + */ + private Runnable interruptAction; + + /** + * 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; + + /** + * 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public Thread(Runnable runnable, String threadName) { + if (threadName == null) { + throw new NullPointerException(); + } + + 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 + * + * @since Android 1.0 + */ + public Thread(String threadName) { + if (threadName == null) { + throw new NullPointerException(); + } + + 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 SecurityException + * if <code>group.checkAccess()</code> fails with a + * SecurityException + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + * + * @since Android 1.0 + */ + 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 SecurityException + * if <code>group.checkAccess()</code> fails with a + * SecurityException + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + * + * @since Android 1.0 + */ + public Thread(ThreadGroup group, Runnable runnable, String threadName) { + if (threadName == null) { + throw new NullPointerException(); + } + + 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 SecurityException + * if <code>group.checkAccess()</code> fails with a + * SecurityException + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + * + * @since Android 1.0 + */ + public Thread(ThreadGroup group, String threadName) { + if (threadName == null) { + throw new NullPointerException(); + } + + 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 SecurityException + * if <code>group.checkAccess()</code> fails with a + * SecurityException + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + * + * @since Android 1.0 + */ + public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { + if (threadName == null) { + throw new NullPointerException(); + } + 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 SecurityException if <code>group.checkAccess()</code> fails + * with a SecurityException + * @throws IllegalThreadStateException if <code>group.destroy()</code> has + * already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + */ + private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + if (group == null) { + group = smgr.getThreadGroup(); + } + + /* + * Freaky security requirement: If the Thread's class is actually + * a subclass of Thread and it tries to override either + * getContextClassLoader() or setContextClassLoader(), the + * SecurityManager has to allow this. + */ + if (getClass() != Thread.class) { + Class[] signature = new Class[] { ClassLoader.class }; + + try { + getClass().getDeclaredMethod("getContextClassLoader", signature); + smgr.checkPermission(new RuntimePermission("enableContextClassLoaderOverride")); + } catch (NoSuchMethodException ex) { + // Ignore. Just interested in the method's existence. + } + + try { + getClass().getDeclaredMethod("setContextClassLoader", signature); + smgr.checkPermission(new RuntimePermission("enableContextClassLoaderOverride")); + } catch (NoSuchMethodException ex) { + // Ignore. Just interested in the method's existence. + } + } + } + + Thread currentThread = Thread.currentThread(); + if (group == null) { + group = currentThread.getThreadGroup(); + } + + group.checkAccess(); + 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(); + + // Transfer over InheritableThreadLocals. + if (currentThread.inheritableValues != null) { + inheritableValues + = new ThreadLocal.Values(currentThread.inheritableValues); + } + + // store current AccessControlContext as inherited context for this thread + SecurityUtils.putContext(this, AccessController.getContext()); + + // 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 + * + * @since Android 1.0 + */ + public static int activeCount() { + return currentThread().getThreadGroup().activeCount(); + } + + /** + * Is used for operations that require approval from a SecurityManager. If + * there's none installed, this method is a no-op. If there's a + * SecurityManager installed, {@link SecurityManager#checkAccess(Thread)} is + * called for that SecurityManager. + * + * @throws SecurityException + * if a SecurityManager is installed and it does not allow + * access to the Thread. + * + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + * + * @since Android 1.0 + */ + public final void checkAccess() { + // Forwards the message to the SecurityManager (if there's one) passing + // the receiver as parameter + + SecurityManager currentManager = System.getSecurityManager(); + if (currentManager != null) { + currentManager.checkAccess(this); + } + } + + /** + * 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. + * + * @since Android 1.0 + */ + @Deprecated + public int countStackFrames() { + return getStackTrace().length; + } + + /** + * Returns the Thread of the caller, that is, the current Thread. + * + * @return the current Thread. + * + * @since Android 1.0 + */ + public static Thread currentThread() { + return VMThread.currentThread(); + } + + /** + * Destroys the receiver without any monitor cleanup. + * + * @deprecated Not implemented. + * + * @since Android 1.0 + */ + @Deprecated + public void destroy() { + throw new NoSuchMethodError("Thread.destroy()"); // TODO Externalize??? + } + + /** + * Prints to the standard error stream a text representation of the current + * stack for this Thread. + * + * @see Throwable#printStackTrace() + * + * @since Android 1.0 + */ + 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 + * @throws SecurityException + * if the installed SecurityManager fails + * {@link SecurityManager#checkAccess(Thread)} + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + * + * @since Android 1.0 + */ + public static int enumerate(Thread[] threads) { + Thread thread = Thread.currentThread(); + thread.checkAccess(); + return thread.getThreadGroup().enumerate(threads); + } + + /** + * <p> + * Returns the stack traces of all the currently live threads and puts them + * into the given map. + * </p> + * + * @return A Map of current Threads to StackTraceElement arrays. + * @throws SecurityException + * if the current SecurityManager fails the + * {@link SecurityManager#checkPermission(java.security.Permission)} + * call. + * + * @since Android 1.0 + */ + public static Map<Thread, StackTraceElement[]> getAllStackTraces() { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + securityManager.checkPermission(new RuntimePermission("getStackTrace")); + securityManager.checkPermission(new RuntimePermission("modifyThreadGroup")); + } + + 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. + * <p> + * If the conditions + * <ol> + * <li>there is a security manager + * <li>the caller's class loader is not null + * <li>the caller's class loader is not the same as the requested + * context class loader and not an ancestor thereof + * </ol> + * are satisfied, a security check for + * <code>RuntimePermission("getClassLoader")</code> is performed first. + * + * @return ClassLoader The context ClassLoader + * @see java.lang.ClassLoader + * @see #getContextClassLoader() + * + * @throws SecurityException + * if the aforementioned security check fails. + * + * @since Android 1.0 + */ + public ClassLoader getContextClassLoader() { + // First, if the conditions + // 1) there is a security manager + // 2) the caller's class loader is not null + // 3) the caller's class loader is not the same as the context + // class loader and not an ancestor thereof + // are satisfied we should perform a security check. + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + ClassLoader calling = VMStack.getCallingClassLoader(); + + if (calling != null && !calling.isAncestorOf(contextClassLoader)) { + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + } + + 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. + * + * @since Android 1.0 + */ + 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. + * + * @since Android 1.0 + */ + public long getId() { + return id; + } + + /** + * Returns the name of the Thread. + * + * @return the Thread's name + * + * @since Android 1.0 + */ + public final String getName() { + return name; + } + + /** + * Returns the priority of the Thread. + * + * @return the Thread's priority + * @see Thread#setPriority + * + * @since Android 1.0 + */ + public final int getPriority() { + return priority; + } + + /** + * Returns the a stack trace representing the current execution state of + * this Thread. + * <p> + * The <code>RuntimePermission("getStackTrace")</code> is checked before + * returning a result. + * </p> + * + * @return an array of StackTraceElements. + * @throws SecurityException + * if the current SecurityManager fails the + * {@link SecurityManager#checkPermission(java.security.Permission)} + * call. + * + * @since Android 1.0 + */ + public StackTraceElement[] getStackTrace() { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + securityManager.checkPermission(new RuntimePermission("getStackTrace")); + } + + StackTraceElement ste[] = VMStack.getThreadStackTrace(this); + return ste != null ? ste : new StackTraceElement[0]; + } + + /** + * Returns the current state of the Thread. This method is useful for + * monitoring purposes. + * + * @return a {@link State} value. + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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}. + * + * @since Android 1.0 + */ + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + if (uncaughtHandler != null) + return uncaughtHandler; + else + return group; // ThreadGroup is instance of UEH + } + + /** + * Posts an interrupt request to this {@code Thread}. Unless the caller is + * the {@link #currentThread()}, the method {@code checkAccess()} is called + * for the installed {@code SecurityManager}, if any. This may result in a + * {@code SecurityException} being thrown. The further 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> + * + * @throws SecurityException + * if <code>checkAccess()</code> fails with a SecurityException + * @see java.lang.SecurityException + * @see java.lang.SecurityManager + * @see Thread#interrupted + * @see Thread#isInterrupted + * + * @since Android 1.0 + */ + public void interrupt() { + checkAccess(); + + if (interruptAction != null) { + interruptAction.run(); + } + + VMThread vmt = this.vmThread; + if (vmt != null) { + vmt.interrupt(); + } + } + + /** + * 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 + * + * @since Android 1.0 + */ + 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 lifeness of the Thread + * @see Thread#start + * + * @since Android 1.0 + */ + public final boolean isAlive() { + Thread.State state = getState(); + + return (state != Thread.State.TERMINATED && state != Thread.State.NEW); + } + + /** + * Returns a <code>boolean</code> indicating whether the receiver is a + * daemon Thread (<code>true</code>) or not (<code>false</code>) A + * daemon Thread only runs as long as there are non-daemon Threads running. + * When the last non-daemon Thread ends, the whole program ends no matter if + * it had daemon Threads still running or not. + * + * @return a <code>boolean</code> indicating whether the Thread is a daemon + * @see Thread#setDaemon + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public final void join() throws InterruptedException { + join(0, 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). + * @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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public final void join(long millis, int nanos) throws InterruptedException { + if (millis < 0 || nanos < 0 || nanos > 999999) { + throw new IllegalArgumentException(); + } + + VMThread t; + + t = this.vmThread; + + if (t != null) { + synchronized (t) { + if (isAlive()) + t.wait(millis, nanos); + } + } + } + + /** + * Resumes a suspended Thread. This is a no-op if the receiver was never + * suspended, or suspended and already resumed. If the receiver is + * suspended, however, makes it resume to the point where it was when it was + * suspended. + * + * @throws SecurityException + * if <code>checkAccess()</code> fails with a SecurityException + * @see Thread#suspend() + * @deprecated Used with deprecated method {@link Thread#suspend} + * + * @since Android 1.0 + */ + @Deprecated + public final void resume() { + checkAccess(); + + VMThread vmt = this.vmThread; + if (vmt != null) { + vmt.resume(); + } + } + + /** + * Calls the <code>run()</code> method of the Runnable object the receiver + * holds. If no Runnable is set, does nothing. + * + * @see Thread#start + * + * @since Android 1.0 + */ + public void run() { + if (target != null) { + target.run(); + } + } + + /** + * Set the context ClassLoader for the receiver. + * <p> + * The <code>RuntimePermission("setContextClassLoader")</code> + * is checked prior to setting the handler. + * </p> + * + * @param cl The context ClassLoader + * @throws SecurityException if the current SecurityManager fails the + * checkPermission call. + * @see java.lang.ClassLoader + * @see #getContextClassLoader() + * + * @since Android 1.0 + */ + public void setContextClassLoader(ClassLoader cl) { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + securityManager.checkPermission(new RuntimePermission("setContextClassLoader")); + } + + contextClassLoader = cl; + } + + /** + * Set if the receiver is a daemon Thread or not. This can only be done + * before the Thread starts running. + * + * @param isDaemon + * indicates whether the Thread should be daemon or not + * @throws SecurityException + * if <code>checkAccess()</code> fails with a SecurityException + * @see Thread#isDaemon + * + * @since Android 1.0 + */ + public final void setDaemon(boolean isDaemon) { + checkAccess(); + + if (hasBeenStarted) { + throw new IllegalThreadStateException("Thread already started."); // TODO Externalize? + } + + if (vmThread == null) { + daemon = isDaemon; + } + } + + /** + * <p> + * Sets the default uncaught exception handler. This handler is invoked in + * case any Thread dies due to an unhandled exception. + * </p> + * <p> + * The <code>RuntimePermission("setDefaultUncaughtExceptionHandler")</code> + * is checked prior to setting the handler. + * </p> + * + * @param handler + * The handler to set or <code>null</code>. + * @throws SecurityException + * if the current SecurityManager fails the checkPermission + * call. + * + * @since Android 1.0 + */ + public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + securityManager.checkPermission(new RuntimePermission ("setDefaultUncaughtExceptionHandler")); + } + + Thread.defaultUncaughtHandler = handler; + } + + /** + * Set the action to be executed when interruption, which is probably be + * used to implement the interruptible channel. The action is null by + * default. And if this method is invoked by passing in a non-null value, + * this action's run() method will be invoked in <code>interrupt()</code>. + * <p> + * This is required internally by NIO, so even if it looks like it's + * useless, don't delete it! + * + * @param action the action to be executed when interruption + */ + @SuppressWarnings("unused") + private void setInterruptAction(Runnable action) { + this.interruptAction = action; + } + + /** + * Sets the name of the Thread. + * + * @param threadName the new name for the Thread + * @throws SecurityException if <code>checkAccess()</code> fails with a + * SecurityException + * @see Thread#getName + * + * @since Android 1.0 + */ + public final void setName(String threadName) { + if (threadName == null) { + throw new NullPointerException(); + } + + checkAccess(); + + 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 the Thread. Note that the final priority set may not + * be the parameter that was passed - it will depend on the receiver's + * ThreadGroup. The priority cannot be set to be higher than the receiver's + * ThreadGroup's maxPriority(). + * + * @param priority + * new priority for the Thread + * @throws SecurityException + * if <code>checkAccess()</code> fails with a SecurityException + * @throws IllegalArgumentException + * if the new priority is greater than Thread.MAX_PRIORITY or + * less than Thread.MIN_PRIORITY + * @see Thread#getPriority + * + * @since Android 1.0 + */ + public final void setPriority(int priority) { + checkAccess(); + + if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) { + throw new IllegalArgumentException("Prioritiy out of range"); // TODO Externalize? + } + + 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>. + * @throws SecurityException + * if the current SecurityManager fails the checkAccess call. + * + * @since Android 1.0 + */ + public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { + checkAccess(); + + 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() + * + * @since Android 1.0 + */ + 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() + * + * @since Android 1.0 + */ + 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 the Thread has been started before + * + * @see Thread#run + * + * @since Android 1.0 + */ + public synchronized void start() { + if (hasBeenStarted) { + throw new IllegalThreadStateException("Thread already started."); // TODO Externalize? + } + + 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. + * + * @throws SecurityException if <code>checkAccess()</code> fails with a + * SecurityException + * @deprecated because stopping a thread in this manner is unsafe and can + * leave your application and the VM in an unpredictable state. + * + * @since Android 1.0 + */ + @Deprecated + public final void stop() { + stop(new ThreadDeath()); + } + + /** + * Requests the receiver Thread to stop and throw the + * <code>throwable()</code>. The Thread is resumed if it was suspended + * and awakened if it was sleeping, so that it can proceed to throw the + * <code>throwable()</code>. + * + * @param throwable Throwable object to be thrown by the Thread + * @throws SecurityException if <code>checkAccess()</code> fails with a + * SecurityException + * @throws NullPointerException if <code>throwable()</code> is + * <code>null</code> + * @deprecated because stopping a thread in this manner is unsafe and can + * leave your application and the VM in an unpredictable state. + * + * @since Android 1.0 + */ + @Deprecated + public final synchronized void stop(Throwable throwable) { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + securityManager.checkAccess(this); + if (Thread.currentThread() != this) { + securityManager.checkPermission(new RuntimePermission("stopThread")); + } + } + + if (throwable == null) { + throw new NullPointerException(); + } + + VMThread vmt = this.vmThread; + if (vmt != null) { + vmt.stop(throwable); + } + } + + /** + * Suspends this Thread. This is a no-op if the receiver is suspended. If + * the receiver <code>isAlive()</code> however, suspended it until <code> + * resume()</code> is sent to it. Suspend requests are not queued, which + * means that N requests are equivalent to just one - only one resume + * request is needed in this case. + * + * @throws SecurityException + * if <code>checkAccess()</code> fails with a SecurityException + * @see Thread#resume() + * @deprecated May cause deadlocks. + * + * @since Android 1.0 + */ + @Deprecated + public final void suspend() { + checkAccess(); + + VMThread vmt = this.vmThread; + if (vmt != null) { + vmt.suspend(); + } + } + + /** + * 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. + * + * @since Android 1.0 + */ + @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. + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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. + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + void uncaughtException(Thread thread, Throwable ex); + } + + /** + * Implementation of <code>unpark()</code>. See {@link LangAccessImpl}. + */ + /*package*/ 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; + } + } + } + } + + /** + * Implementation of <code>parkFor()</code>. See {@link LangAccessImpl}. + * This method must only be called when <code>this</code> is the current + * thread. + * + * @param nanos number of nanoseconds to park for + */ + /*package*/ 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 / 1000000; + nanos %= 1000000; + + 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"); + } + } + } + } + + /** + * Implementation of <code>parkUntil()</code>. See {@link LangAccessImpl}. + * This method must only be called when <code>this</code> is the current + * thread. + * + * @param time absolute milliseconds since the epoch to park until + */ + /*package*/ 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 * 1000000); + } + } + } +} diff --git a/luni-kernel/src/main/java/java/lang/ThreadGroup.java b/luni-kernel/src/main/java/java/lang/ThreadGroup.java new file mode 100644 index 0000000..33fa31e --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ThreadGroup.java @@ -0,0 +1,926 @@ +/* + * 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; + +/** + * A {@code ThreadGroup} is a means of organizing {@link Thread}s into a + * hierarchical structure. A {@code ThreadGroup} can contain zero or more + * {@code Thread}s and zero or more other {@code ThreadGroup}s. Each {@code + * Thread} and each {@code ThreadGroup} (except the root group) has a unique + * parent {@code ThreadGroup}. The result is a tree whose inner nodes are + * {@code ThreadGroup}s and whose leaf nodes are {@code Threads}. The unique + * root of the tree is a {@code ThreadGroup} that is created at VM startup and + * has the name "system". The benefit of using {@code ThreadGroup}s, in addition + * to the mere housekeeping aspect, is that all {@code Thread}s in a {@code + * ThreadGroup} can be manipulated together, that is, the {@code ThreadGroup} + * has methods that delegate to all its all {@code Thread}s. + * + * @see Thread + * @see SecurityManager + * + * @since Android 1.0 + */ +public class ThreadGroup implements Thread.UncaughtExceptionHandler { + + // Name of this ThreadGroup + private String name; + // BEGIN android-note + // VM needs this field name for debugging. + // END android-note + + // Maximum priority for Threads inside this ThreadGroup + private int maxPriority = Thread.MAX_PRIORITY; + + // The ThreadGroup to which this ThreadGroup belongs + ThreadGroup parent; + // BEGIN android-note + // VM needs this field name for debugging. + // END android-note + + int numThreads; + + // The Threads this ThreadGroup contains + private Thread[] childrenThreads = new Thread[5]; + + // The number of children groups + int numGroups; + + // The ThreadGroups this ThreadGroup contains + private ThreadGroup[] childrenGroups = new ThreadGroup[3]; + + // Locked when using the childrenGroups field + private class ChildrenGroupsLock {} + private Object childrenGroupsLock = new ChildrenGroupsLock(); + + // Locked when using the childrenThreads field + private class ChildrenThreadsLock {} + private Object childrenThreadsLock = new ChildrenThreadsLock(); + + // Whether this ThreadGroup is a daemon ThreadGroup or not + private boolean isDaemon; + + // Whether this ThreadGroup has already been destroyed or not + private boolean isDestroyed; + + // BEGIN android-added + /* the VM uses these directly; do not rename */ + static ThreadGroup mSystem = new ThreadGroup(); + static ThreadGroup mMain = new ThreadGroup(mSystem, "main"); + // END android-added + + // BEGIN android-removed + // /** + // * Used by the JVM to create the "system" ThreadGroup. Construct a + // * ThreadGroup instance, and assign the name "system". + // */ + // private ThreadGroup() { + // name = "system"; + // } + // END android-removed + + /** + * Constructs a new ThreadGroup with the name provided. The new ThreadGroup + * will be child of the ThreadGroup to which the + * {@code Thread.currentThread()} belongs. + * + * @param name the name for the ThreadGroup being created + * + * @throws SecurityException if {@code checkAccess()} for the parent + * group fails with a SecurityException + * + * @see java.lang.Thread#currentThread + */ + + public ThreadGroup(String name) { + this(Thread.currentThread().getThreadGroup(), name); + } + + /** + * Constructs a new ThreadGroup with the name provided, as child of the + * ThreadGroup {@code parent}. + * + * @param parent the parent ThreadGroup + * @param name the name for the ThreadGroup being created + * + * @throws NullPointerException if {@code parent} is + * {@code null} + * @throws SecurityException if {@code checkAccess()} for the parent + * group fails with a SecurityException + * @throws IllegalThreadStateException if {@code parent} has been + * destroyed already + */ + public ThreadGroup(ThreadGroup parent, String name) { + super(); + if (Thread.currentThread() != null) { + // If parent is null we must throw NullPointerException, but that + // will be done "for free" with the message send below + parent.checkAccess(); + } + + this.name = name; + this.setParent(parent); + if (parent != null) { + 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. + */ + ThreadGroup() { + this.name = "system"; + this.setParent(null); + } + + /** + * Returns the number of Threads which are children of the receiver, + * directly or indirectly and are running. + * + * @return the number of children Threads + */ + + public int activeCount() { + // BEGIN android-changed + int count = 0; + // Lock the children thread list + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + if(childrenThreads[i].isAlive()) { + count++; + } + } + } + // END android-changed + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + count += this.childrenGroups[i].activeCount(); + } + } + return count; + } + + /** + * Returns the number of ThreadGroups which are children of the receiver, + * directly or indirectly. + * + * @return the number of children ThreadGroups + */ + public int activeGroupCount() { + int count = 0; + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + // One for this group & the subgroups + count += 1 + this.childrenGroups[i].activeGroupCount(); + } + } + return count; + } + + /** + * Adds a Thread to the receiver. This should only be visible to class + * java.lang.Thread, and should only be called when a new Thread is created + * and initialized by the constructor. + * + * @param thread Thread to add to the receiver + * + * @throws IllegalThreadStateException if the receiver has been destroyed + * already + * + * @see #remove(java.lang.Thread) + */ + final void add(Thread thread) throws IllegalThreadStateException { + synchronized (this.childrenThreadsLock) { + if (!isDestroyed) { + if (childrenThreads.length == numThreads) { + Thread[] newThreads = new Thread[childrenThreads.length * 2]; + System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads); + newThreads[numThreads++] = thread; + childrenThreads = newThreads; + } else { + childrenThreads[numThreads++] = thread; + } + } else { + throw new IllegalThreadStateException(); + } + } + } + + /** + * Adds a ThreadGroup to the receiver. + * + * @param g ThreadGroup to add to the receiver + * + * @throws IllegalThreadStateException if the receiver has been destroyed + * already + */ + private void add(ThreadGroup g) throws IllegalThreadStateException { + synchronized (this.childrenGroupsLock) { + if (!isDestroyed) { + if (childrenGroups.length == numGroups) { + ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2]; + System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups); + newGroups[numGroups++] = g; + childrenGroups = newGroups; + } else { + childrenGroups[numGroups++] = g; + } + } else { + throw new IllegalThreadStateException(); + } + } + } + + /** + * 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; + } + + /** + * Checks the accessibility of the ThreadGroup from the perspective of the + * caller. If there is a SecurityManager installed, calls + * {@code checkAccess} with the receiver as a parameter, otherwise does + * nothing. + */ + public final void checkAccess() { + // Forwards the message to the SecurityManager (if there's one) passing + // the receiver as parameter + SecurityManager currentManager = System.getSecurityManager(); + if (currentManager != null) { + currentManager.checkAccess(this); + } + } + + /** + * Destroys the receiver and recursively all its subgroups. It is only legal + * to destroy a ThreadGroup that has no Threads in it. Any daemon + * ThreadGroup is destroyed automatically when it becomes empty (no Threads + * and no ThreadGroups in it). + * + * @throws IllegalThreadStateException if the receiver or any of its + * subgroups has been destroyed already or if it still contains + * threads. + * @throws SecurityException if {@code this.checkAccess()} fails with + * a SecurityException + */ + + public final void destroy() { + checkAccess(); + + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + synchronized (this.childrenGroupsLock) { + // BEGIN android-added + if (this.isDestroyed) { + throw new IllegalThreadStateException( + "Thread group was already destroyed: " + + (this.name != null ? this.name : "n/a")); + } + if (this.numThreads > 0) { + throw new IllegalThreadStateException( + "Thread group still contains threads: " + + (this.name != null ? this.name : "n/a")); + } + // END android-added + int toDestroy = numGroups; + // Call recursively for subgroups + for (int i = 0; i < toDestroy; i++) { + // We always get the first element - remember, when the + // child dies it removes itself from our collection. See + // below. + this.childrenGroups[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 the receiver and recursively all its + * subgroups if the receiver is a daemon ThreadGroup. + * + * @see #destroy + * @see #setDaemon + * @see #isDaemon + */ + private void destroyIfEmptyDaemon() { + // Has to be non-destroyed daemon to make sense + synchronized (this.childrenThreadsLock) { + if (isDaemon && !isDestroyed && numThreads == 0) { + synchronized (this.childrenGroupsLock) { + if (numGroups == 0) { + 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. + * + * @param threads the array into which the Threads will be copied + * @return the number of Threads 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. + * + * @param threads the array into which the Threads will be copied + * @param recurse indicates whether Threads in subgroups should be + * recursively copied as well + * @return the number of Threads 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. + * + * @param groups the array into which the ThreadGroups will be copied + * @return the number of ThreadGroups 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 and stores the groups in the given array. Returns when + * the array is full or no more groups remain, whichever happens first. + * + * @param groups the array into which the ThreadGroups will be copied + * @param recurse indicates whether ThreadGroups in subgroups should be + * recursively copied as well or not + * @return the number of ThreadGroups 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) { + checkAccess(); + + Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads + : (Object[]) childrenGroups; + Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock; + + synchronized (syncLock) { // Lock this subpart of the tree as we walk + for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) { + if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumeration[enumerationIndex++] = immediateCollection[i]; + } + } + } + + if (recurse) { // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse, + enumerationIndex, enumeratingThreads); + } + } + } + return enumerationIndex; + } + + /** + * Returns the maximum allowed priority for a Thread in the receiver. + * + * @return the maximum priority + * + * @see #setMaxPriority + */ + public final int getMaxPriority() { + return maxPriority; + } + + /** + * Returns the name of the receiver. + * + * @return the receiver's name + */ + public final String getName() { + return name; + } + + /** + * Returns the receiver's parent ThreadGroup. It can be {@code null} if the + * receiver is the the root ThreadGroup. + * + * @return the parent ThreadGroup + * + */ + public final ThreadGroup getParent() { + if (parent != null) { + parent.checkAccess(); + } + return parent; + } + + /** + * Interrupts every Thread in the receiver and recursively in all its + * subgroups. + * + * @throws SecurityException if {@code this.checkAccess()} fails with + * a SecurityException + * + * @see Thread#interrupt + */ + public final void interrupt() { + checkAccess(); + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + this.childrenThreads[i].interrupt(); + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].interrupt(); + } + } + } + + /** + * Checks whether the receiver is a daemon ThreadGroup. + * + * @return true if (and only if) the receiver is a daemon ThreadGroup + * + * @see #setDaemon + * @see #destroy + */ + public final boolean isDaemon() { + return isDaemon; + } + + /** + * Checks whether the receiver has already been destroyed. + * + * @return true if (and only if) the receiver has already been destroyed + * + * @see #destroy + */ + public synchronized boolean isDestroyed() { + return isDestroyed; + } + + /** + * Outputs to {@code System.out} a text representation of the + * hierarchy of Threads and ThreadGroups in the receiver (and recursively). + * Proper indentation is done to suggest 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 the receiver (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) { + for (int i = 0; i < levels; i++) { + System.out.print(" "); // 4 spaces for each level + } + + // Print the receiver + System.out.println(this.toString()); + + // Print the children threads, with 1 extra indentation + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + // children get an extra indentation, 4 spaces for each level + for (int j = 0; j <= levels; j++) { + System.out.print(" "); + } + System.out.println(this.childrenThreads[i]); + } + } + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].list(levels + 1); + } + } + } + + /** + * Checks whether the receiver is a direct or indirect parent group of a + * given ThreadGroup. + * + * @param g the potential child ThreadGroup + * + * @return true if (and only if) the receiver 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 a Thread from the receiver. This should only be visible to class + * java.lang.Thread, and should only be called when a Thread dies. + * + * @param thread Thread to remove from the receiver + * + * @see #add(Thread) + */ + final void remove(java.lang.Thread thread) { + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + if (childrenThreads[i].equals(thread)) { + numThreads--; + System + .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads + - i); + childrenThreads[numThreads] = null; + break; + } + } + } + destroyIfEmptyDaemon(); + } + + /** + * Removes an immediate subgroup from the receiver. + * + * @param g ThreadGroup to remove from the receiver + * + * @see #add(Thread) + * @see #add(ThreadGroup) + */ + private void remove(ThreadGroup g) { + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + if (childrenGroups[i].equals(g)) { + numGroups--; + System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i); + childrenGroups[numGroups] = null; + break; + } + } + } + destroyIfEmptyDaemon(); + } + + /** + * Resumes every Thread in the receiver and recursively in all its + * subgroups. + * + * @throws SecurityException if {@code this.checkAccess()} fails with + * a SecurityException + * + * @see Thread#resume + * @see #suspend + * + * @deprecated Requires deprecated method Thread.resume(). + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void resume() { + checkAccess(); + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + this.childrenThreads[i].resume(); + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].resume(); + } + } + } + + /** + * Configures the receiver to be a daemon ThreadGroup or not. Daemon + * ThreadGroups are automatically destroyed when they become empty. + * + * @param isDaemon the new value defining if receiver should be daemon or + * not + * + * @throws SecurityException if {@code checkAccess()} for the parent + * group fails with a SecurityException + * + * @see #isDaemon + * @see #destroy + */ + public final void setDaemon(boolean isDaemon) { + checkAccess(); + this.isDaemon = isDaemon; + } + + /** + * Configures the maximum allowed priority for a Thread in the receiver and + * recursively in all its subgroups. + * + * One can never change the maximum priority of a ThreadGroup to be higher + * than it was. Such an attempt will not result in an exception, it will + * simply leave the ThreadGroup with its current maximum priority. + * + * @param newMax the new maximum priority to be set + * + * @throws SecurityException if {@code checkAccess()} fails with a + * SecurityException + * @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) { + checkAccess(); + + 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; + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + // ??? why not maxPriority + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].setMaxPriority(newMax); + } + } + } + } + + /** + * Sets the parent ThreadGroup of the receiver, and adds the receiver to the + * parent's collection of immediate children (if {@code parent} is + * not {@code null}). + * + * @param parent The parent ThreadGroup, or null if the receiver is to be + * the root ThreadGroup + * + * @see #getParent + * @see #parentOf + */ + private void setParent(ThreadGroup parent) { + if (parent != null) { + parent.add(this); + } + this.parent = parent; + } + + /** + * Stops every Thread in the receiver and recursively in all its subgroups. + * + * @throws SecurityException if {@code this.checkAccess()} fails with + * a SecurityException + * + * @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(); + } + } + + /** + * @deprecated Requires deprecated method Thread.suspend(). + */ + @SuppressWarnings("deprecation") + @Deprecated + private final boolean stopHelper() { + checkAccess(); + + boolean stopCurrent = false; + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + Thread current = Thread.currentThread(); + for (int i = 0; i < numThreads; i++) { + if (this.childrenThreads[i] == current) { + stopCurrent = true; + } else { + this.childrenThreads[i].stop(); + } + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + stopCurrent |= this.childrenGroups[i].stopHelper(); + } + } + return stopCurrent; + } + + /** + * Suspends every Thread in the receiver and recursively in all its + * subgroups. + * + * @throws SecurityException if {@code this.checkAccess()} fails with + * a SecurityException + * + * @see Thread#suspend + * @see #resume + * + * @deprecated Requires deprecated method Thread.suspend(). + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void suspend() { + if (suspendHelper()) { + Thread.currentThread().suspend(); + } + } + + /** + * @deprecated Requires deprecated method Thread.suspend(). + */ + @SuppressWarnings("deprecation") + @Deprecated + private final boolean suspendHelper() { + checkAccess(); + + boolean suspendCurrent = false; + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + Thread current = Thread.currentThread(); + for (int i = 0; i < numThreads; i++) { + if (this.childrenThreads[i] == current) { + suspendCurrent = true; + } else { + this.childrenThreads[i].suspend(); + } + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + suspendCurrent |= this.childrenGroups[i].suspendHelper(); + } + } + return suspendCurrent; + } + + /** + * Returns a string containing a concise, human-readable description of the + * receiver. + * + * @return a printable representation of the ThreadGroup + */ + @Override + public String toString() { + return getClass().getName() + "[name=" + this.getName() + ",maxpri=" + + this.getMaxPriority() + "]"; + } + + /** + * Handles uncaught exceptions. Any uncaught exception in any Thread + * is forwarded (by the VM) to the Thread's ThreadGroup by sending this + * message (uncaughtException). This allows users to define custom + * ThreadGroup classes and custom behavior for when a Thread has an + * uncaughtException or when it does (ThreadDeath). + * + * @param t the Thread that terminated with an uncaught exception + * @param e the uncaught exception itself + * + * @see Thread#stop() + * @see Thread#stop(Throwable) + * @see ThreadDeath + */ + public void uncaughtException(Thread t, Throwable e) { + // BEGIN android-changed + 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); + } + // END android-changed + } + + // BEGIN android-added + /** + * Non-standard method for adding a thread to a group, required by Dalvik. + * + * @param thread Thread to add to the receiver + * + * @throws IllegalThreadStateException if the receiver has been destroyed + * already + * + * @see #add(java.lang.Thread) + * @see #removeThread(java.lang.Thread) + */ + void addThread(Thread thread) throws IllegalThreadStateException { + add(thread); + } + + /** + * Non-standard method for adding a thread to a group, required by Dalvik. + * + * @param thread Thread to add to the receiver + * + * @throws IllegalThreadStateException if the receiver has been destroyed + * already + * + * @see #remove(java.lang.Thread) + * @see #addThread(java.lang.Thread) + */ + void removeThread(Thread thread) throws IllegalThreadStateException { + remove(thread); + } + // END android-added +} diff --git a/luni-kernel/src/main/java/java/lang/Throwable.java b/luni-kernel/src/main/java/java/lang/Throwable.java new file mode 100644 index 0000000..12b4e9b --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/Throwable.java @@ -0,0 +1,410 @@ +/* + * 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.io.IOException; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * The superclass of all classes which can be thrown by the virtual machine. The + * two direct subclasses are recoverable exceptions ({@code Exception}) and + * unrecoverable errors ({@code Error}). This class provides common methods for + * accessing a string message which provides extra information about the + * circumstances in which the {@code Throwable} was created (basically an error + * message in most cases), and for saving a stack trace (that is, a record of + * the call stack at a particular point in time) which can be printed later. + * <p> + * A {@code Throwable} can also include a cause, which is a nested {@code + * Throwable} that represents the original problem that led to this {@code + * Throwable}. It is often used for wrapping various types of errors into a + * common {@code Throwable} without losing the detailed original error + * information. When printing the stack trace, the trace of the cause is + * included. + * + * @see Error + * @see Exception + * @see RuntimeException + * + * @since Android 1.0 + */ +public class Throwable implements java.io.Serializable { + private static final long serialVersionUID = -3042686055658047285L; + + /** + * The message provided when the exception was created. + */ + private String detailMessage; + + /** + * The cause of this Throwable. Null when there is no cause. + */ + private Throwable cause = this; + + // BEGIN android-added + /** + * An intermediate representation of the stack trace. This field may + * be accessed by the VM; do not rename. + */ + private volatile Object stackState; + // END android-added + + /** + * A fully-expanded representation of the stack trace. + */ + private StackTraceElement[] stackTrace; + + /** + * Constructs a new {@code Throwable} that includes the current stack trace. + * + * @since Android 1.0 + */ + public Throwable() { + super(); + fillInStackTrace(); + } + + /** + * Constructs a new {@code Throwable} with the current stack trace and the + * specified detail message. + * + * @param detailMessage + * the detail message for this {@code Throwable}. + * @since Android 1.0 + */ + public Throwable(String detailMessage) { + this(); + this.detailMessage = detailMessage; + } + + /** + * Constructs a new {@code Throwable} with the current stack trace, the + * specified detail message and the specified cause. + * + * @param detailMessage + * the detail message for this {@code Throwable}. + * @param throwable + * the cause of this {@code Throwable}. + * @since Android 1.0 + */ + public Throwable(String detailMessage, Throwable throwable) { + this(); + this.detailMessage = detailMessage; + cause = throwable; + } + + /** + * Constructs a new {@code Throwable} with the current stack trace and the + * specified cause. + * + * @param throwable + * the cause of this {@code Throwable}. + * @since Android 1.0 + */ + public Throwable(Throwable throwable) { + this(); + this.detailMessage = throwable == null ? null : throwable.toString(); + cause = throwable; + } + + // BEGIN android-changed + /** + * Records the stack trace from the point where this method has been called + * to this {@code Throwable}. The method is public so that code which + * catches a {@code Throwable} and then re-throws it can adjust the stack + * trace to represent the location where the exception was re-thrown. + * + * @return this {@code Throwable} instance. + * @since Android 1.0 + */ + public Throwable fillInStackTrace() { + // Fill in the intermediate representation + stackState = nativeFillInStackTrace(); + // Mark the full representation as empty + stackTrace = null; + return this; + } + // END android-changed + + /** + * Returns the extra information message which was provided when this + * {@code Throwable} was created. Returns {@code null} if no message was + * provided at creation time. + * + * @return this {@code Throwable}'s detail message. + * @since Android 1.0 + */ + public String getMessage() { + return detailMessage; + } + + /** + * Returns the extra information message which was provided when this + * {@code Throwable} was created. Returns {@code null} if no message was + * provided at creation time. Subclasses may override this method to return + * localized text for the message. The Android reference implementation + * returns the unlocalized detail message. + * + * @return this {@code Throwable}'s localized detail message. + * @since Android 1.0 + */ + public String getLocalizedMessage() { + return getMessage(); + } + + /** + * Returns the array of stack trace elements of this {@code Throwable}. Each + * {@code StackTraceElement} represents an entry in the call stack. The + * element at position 0 is the top of the stack, that is, the stack frame + * where this {@code Throwable} is thrown. + * + * @return a copy of the array of {@code StackTraceElement}s representing + * the call stack. Changes in the array obtained from this call will + * not change the call stack stored in this {@code Throwable}. + * @see #printStackTrace() + * @since Android 1.0 + */ + public StackTraceElement[] getStackTrace() { + return getInternalStackTrace().clone(); + } + + /** + * Sets the array of stack trace elements. Each {@code StackTraceElement} + * represents an entry in the call stack. A copy of the specified array is + * stored in this {@code Throwable}. will be returned by {@code + * getStackTrace()} and printed by {@code printStackTrace()}. + * + * @param trace + * the new array of {@code StackTraceElement}s. A copy of the + * array is stored in this {@code Throwable}, so subsequent + * changes to {@code trace} will not change the call stack stored + * in this {@code Throwable}. + * @throws NullPointerException + * if any element in {@code trace} is {@code null}. + * @see #printStackTrace() + * @since Android 1.0 + */ + public void setStackTrace(StackTraceElement[] trace) { + StackTraceElement[] newTrace = trace.clone(); + for (java.lang.StackTraceElement element : newTrace) { + if (element == null) { + throw new NullPointerException(); + } + } + stackTrace = newTrace; + } + + /** + * Writes a printable representation of this {@code Throwable}'s stack trace + * to the {@code System.err} stream. + * + * @since Android 1.0 + */ + public void printStackTrace() { + printStackTrace(System.err); + } + + /** + * Counts the number of duplicate stack frames, starting from the + * end of the stack. + * + * @param currentStack a stack to compare + * @param parentStack a stack to compare + * + * @return the number of duplicate stack frames. + */ + private static int countDuplicates(StackTraceElement[] currentStack, + StackTraceElement[] parentStack) { + int duplicates = 0; + int parentIndex = parentStack.length; + for (int i = currentStack.length; --i >= 0 && --parentIndex >= 0;) { + StackTraceElement parentFrame = parentStack[parentIndex]; + if (parentFrame.equals(currentStack[i])) { + duplicates++; + } else { + break; + } + } + return duplicates; + } + + /** + * Returns an array of StackTraceElement. Each StackTraceElement + * represents a entry on the stack. + * + * @return an array of StackTraceElement representing the stack + */ + private StackTraceElement[] getInternalStackTrace() { + if (stackTrace == null) { + // BEGIN android-changed + stackTrace = nativeGetStackTrace(stackState); + stackState = null; // Clean up intermediate representation + // END android-changed + } + return stackTrace; + } + + /** + * Writes a printable representation of this {@code Throwable}'s stack trace + * to the specified print stream. If the {@code Throwable} contains a + * {@link #getCause() cause}, the method will be invoked recursively for + * the nested {@code Throwable}. + * + * @param err + * the stream to write the stack trace on. + * @since Android 1.0 + */ + public void printStackTrace(PrintStream err) { + err.println(toString()); + // Don't use getStackTrace() as it calls clone() + // Get stackTrace, in case stackTrace is reassigned + StackTraceElement[] stack = getInternalStackTrace(); + for (java.lang.StackTraceElement element : stack) { + err.println("\tat " + element); + } + + StackTraceElement[] parentStack = stack; + Throwable throwable = getCause(); + while (throwable != null) { + err.print("Caused by: "); + err.println(throwable); + StackTraceElement[] currentStack = throwable.getInternalStackTrace(); + int duplicates = countDuplicates(currentStack, parentStack); + for (int i = 0; i < currentStack.length - duplicates; i++) { + err.println("\tat " + currentStack[i]); + } + if (duplicates > 0) { + err.println("\t... " + duplicates + " more"); + } + parentStack = currentStack; + throwable = throwable.getCause(); + } + } + + /** + * Writes a printable representation of this {@code Throwable}'s stack trace + * to the specified print writer. If the {@code Throwable} contains a + * {@link #getCause() cause}, the method will be invoked recursively for the + * nested {@code Throwable}. + * + * @param err + * the writer to write the stack trace on. + * @since Android 1.0 + */ + public void printStackTrace(PrintWriter err) { + err.println(toString()); + // Don't use getStackTrace() as it calls clone() + // Get stackTrace, in case stackTrace is reassigned + StackTraceElement[] stack = getInternalStackTrace(); + for (java.lang.StackTraceElement element : stack) { + err.println("\tat " + element); + } + + StackTraceElement[] parentStack = stack; + Throwable throwable = getCause(); + while (throwable != null) { + err.print("Caused by: "); + err.println(throwable); + StackTraceElement[] currentStack = throwable.getInternalStackTrace(); + int duplicates = countDuplicates(currentStack, parentStack); + for (int i = 0; i < currentStack.length - duplicates; i++) { + err.println("\tat " + currentStack[i]); + } + if (duplicates > 0) { + err.println("\t... " + duplicates + " more"); + } + parentStack = currentStack; + throwable = throwable.getCause(); + } + } + + @Override + public String toString() { + String msg = getLocalizedMessage(); + String name = getClass().getName(); + if (msg == null) { + return name; + } + return new StringBuffer(name.length() + 2 + msg.length()).append(name).append(": ") + .append(msg).toString(); + } + + /** + * Initializes the cause of this {@code Throwable}. The cause can only be + * initialized once. + * + * @param throwable + * the cause of this {@code Throwable}. + * @return this {@code Throwable} instance. + * @throws IllegalArgumentException + * if {@code Throwable} is this object. + * @throws IllegalStateException + * if the cause has already been initialized. + * @since Android 1.0 + */ + public Throwable initCause(Throwable throwable) { + // BEGIN android-note + // removed synchronized modifier + // END android-note + if (cause == this) { + if (throwable != this) { + cause = throwable; + return this; + } + throw new IllegalArgumentException("Cause cannot be the receiver"); + } + throw new IllegalStateException("Cause already initialized"); + } + + /** + * Returns the cause of this {@code Throwable}, or {@code null} if there is + * no cause. + * + * @return Throwable this {@code Throwable}'s cause. + * @since Android 1.0 + */ + public Throwable getCause() { + if (cause == this) { + return null; + } + return cause; + } + + private void writeObject(ObjectOutputStream s) throws IOException { + // ensure the stackTrace field is initialized + getInternalStackTrace(); + s.defaultWriteObject(); + } + + // BEGIN android-added + /* + * Creates a compact, VM-specific collection of goodies, suitable for + * storing in the "stackState" field, based on the current thread's + * call stack. + */ + native private static Object nativeFillInStackTrace(); + + /* + * Creates an array of StackTraceElement objects from the data held + * in "stackState". + */ + native private static StackTraceElement[] nativeGetStackTrace(Object stackState); + // END android-added +} + diff --git a/luni-kernel/src/main/java/java/lang/ref/PhantomReference.java b/luni-kernel/src/main/java/java/lang/ref/PhantomReference.java new file mode 100644 index 0000000..c33ab6e --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ref/PhantomReference.java @@ -0,0 +1,81 @@ +/* + * 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.ref; + +/** + * Implements a phantom reference, which is the weakest of the three types of + * references. Once the garbage collector decides that an object {@code obj} is + * <a href="package.html#definitions>phantom-reachable</a>, it is being enqueued + * on the corresponding queue, but its referent is not cleared. That is, the + * reference queue of the phantom reference must explicitly be processed by some + * application code. As a consequence, a phantom reference that is not + * registered with any reference queue does not make any sense. + * <p> + * Phantom references are useful for implementing cleanup operations that are + * necessary before an object gets garbage-collected. They are sometimes more + * flexible than the {@link Object#finalize()} method. + * + * @since Android 1.0 + */ +public class PhantomReference<T> extends Reference<T> { + + /** + * Constructs a new phantom reference and registers it with the given + * reference queue. The reference queue may be {@code null}, but this case + * does not make any sense, since the reference will never be enqueued, and + * the {@link #get()} method always returns {@code null}. + * + * @param r the referent to track + * @param q the queue to register the phantom reference object with + * + * @since Android 1.0 + */ + public PhantomReference(T r, ReferenceQueue<? super T> q) { + super(); + referent = r; + queue = q; + } + + /** + * Returns {@code null}. The referent of a phantom reference is not + * accessible. + * + * @return {@code null} (always) + * + * @since Android 1.0 + */ + @Override + public T get() { + return null; + } +} diff --git a/luni-kernel/src/main/java/java/lang/ref/Reference.java b/luni-kernel/src/main/java/java/lang/ref/Reference.java new file mode 100644 index 0000000..ca7290b --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ref/Reference.java @@ -0,0 +1,162 @@ +/* + * 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.ref; + +/** + * Provides an abstract class which describes behavior common to all reference + * objects. It is not possible to create immediate subclasses of + * {@code Reference} in addition to the ones provided by this package. It is + * also not desirable to do so, since references require very close cooperation + * with the system's garbage collector. The existing, specialized reference + * classes should be used instead. + * + * @since Android 1.0 + */ +public abstract class Reference<T> { + + /** + * The object to which this reference refers. + * VM requirement: this field <em>must</em> be called "referent" + * and be an object. + */ + T referent; + + /** + * If non-null, the queue on which this reference will be enqueued + * when the referent is appropriately reachable. + * VM requirement: this field <em>must</em> be called "queue" + * and be a java.lang.ref.ReferenceQueue. + */ + @SuppressWarnings("unchecked") + ReferenceQueue queue; + + /** + * Used internally by java.lang.ref.ReferenceQueue. + * VM requirement: this field <em>must</em> be called "queueNext" + * and be a java.lang.ref.Reference. + */ + @SuppressWarnings("unchecked") + Reference queueNext; + + /** + * Used internally by Dalvik. + * VM requirement: this field <em>must</em> be called "vmData" + * and be an int. + */ + @SuppressWarnings("unused") + private int vmData; + + /** + * Constructs a new instance of this class. + */ + Reference() { + super(); + } + + /** + * Makes the referent {@code null}. This does not force the reference + * object to be enqueued. + * + * @since Android 1.0 + */ + public void clear() { + referent = null; + } + + /** + * An implementation of .enqueue() that is safe for the VM to call. + * If a Reference object is a subclass of any of the + * java.lang.ref.*Reference classes and that subclass overrides enqueue(), + * the VM may not call the overridden method. + * VM requirement: this method <em>must</em> be called "enqueueInternal", + * have the signature "()Z", and be private. + * + * @return {@code true} if this call has caused the {@code Reference} to + * become enqueued, or {@code false} otherwise + * + * @since Android 1.0 + */ + @SuppressWarnings("unchecked") + private synchronized boolean enqueueInternal() { + /* VM requirement: + * The VM assumes that this function only does work + * if "(queue != null && queueNext == null)". + * If that changes, Dalvik needs to change, too. + * (see MarkSweep.c:enqueueReference()) + */ + if (queue != null && queueNext == null) { + queue.enqueue(this); + queue = null; + return true; + } + return false; + } + + /** + * Forces the reference object to be enqueued if it has been associated with + * a queue. + * + * @return {@code true} if this call has caused the {@code Reference} to + * become enqueued, or {@code false} otherwise + * + * @since Android 1.0 + */ + public boolean enqueue() { + return enqueueInternal(); + } + + /** + * Returns the referent of the reference object. + * + * @return the referent to which reference refers, or {@code null} if the + * object has been cleared. + * + * @since Android 1.0 + */ + public T get() { + return referent; + } + + /** + * Checks whether the reference object has been enqueued. + * + * @return {@code true} if the {@code Reference} has been enqueued, {@code + * false} otherwise + * + * @since Android 1.0 + */ + public boolean isEnqueued() { + return queueNext != null; + } + +} diff --git a/luni-kernel/src/main/java/java/lang/ref/SoftReference.java b/luni-kernel/src/main/java/java/lang/ref/SoftReference.java new file mode 100644 index 0000000..19253f1 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ref/SoftReference.java @@ -0,0 +1,135 @@ +/* + * 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.ref; + +/** + * Implements a soft reference, which is the least-weak of the three types of + * references. Once the garbage collector has decided that an object {@code obj} + * is <a href="package.html#definitions>softly-reachable</a>, the following + * may happen, either immediately or at a later point: + * + * <ul> + * <li> + * A set {@code ref} of references is determined. {@code ref} contains the + * following elements: + * <ul> + * <li> + * All soft references pointing to {@code obj}. + * </li> + * <li> + * All soft references pointing to objects from which {@code obj} is + * strongly reachable. + * </li> + * </ul> + * </li> + * <li> + * All references in {@code ref} are atomically cleared. + * </li> + * <li> + * At the same time or some time in the future, all references in {@code + * ref} will be enqueued with their corresponding reference queues, if any. + * </li> + * </ul> + * + * The system may decide not to clear and enqueue soft references until a later + * time, yet all {@code SoftReference}s pointing to softly reachable objects are + * guaranteed to be cleared before the VM will throw an {@link + * java.lang.OutOfMemoryError}. + * + * Soft references are useful for caches that should automatically have + * their entries removed once they are not referenced any more (from outside), + * and there is a need for memory. The difference between a {@code + * SoftReference} and a {@code WeakReference} is the point of time at which the + * decision is made to clear and enqueue the reference: + * + * <ul> + * <li> + * A {@code SoftReference} should be cleared and enqueued <em>as late as + * possible</em>, that is, in case the VM is in danger of running out of + * memory. + * </li> + * <li> + * A {@code WeakReference} may be cleared and enqueued as soon as is + * known to be weakly-referenced. + * </li> + * </ul> + * + * @since Android 1.0 + */ +public class SoftReference<T> extends Reference<T> { + + /** + * Constructs a new soft reference to the given referent. The newly created + * reference is not registered with any reference queue. + * + * @param r the referent to track + * + * @since Android 1.0 + */ + public SoftReference(T r) { + super(); + referent = r; + } + + /** + * Constructs a new soft reference to the given referent. The newly created + * reference is registered with the given reference queue. + * + * @param r the referent to track + * @param q the queue to register to the reference object with. A null value + * results in a weak reference that is not associated with any + * queue. + * + * @since Android 1.0 + */ + public SoftReference(T r, ReferenceQueue<? super T> q) { + super(); + referent = r; + queue = q; + } + +// BEGIN android-removed +// /** +// * Return the referent of the reference object. +// * +// * @return the referent to which reference refers, or {@code null} if the +// * referent has been cleared. +// * +// * @since Android 1.0 +// */ +// @Override +// public T get() { +// return super.get(); +// } +// END android-removed +} diff --git a/luni-kernel/src/main/java/java/lang/ref/WeakReference.java b/luni-kernel/src/main/java/java/lang/ref/WeakReference.java new file mode 100644 index 0000000..813cc89 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/ref/WeakReference.java @@ -0,0 +1,119 @@ +/* + * 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.ref; + +/** + * Implements a weak reference, which is the middle of the three types of + * references. Once the garbage collector decides that an object {@code obj} is + * is <a href="package.html#definitions>weakly-reachable</a>, the following + * happens: + * + * <ul> + * <li> + * A set {@code ref} of references is determined. {@code ref} contains the + * following elements: + * <ul> + * <li> + * All weak references pointing to {@code obj}. + * </li> + * <li> + * All weak references pointing to objects from which {@code obj} is + * either strongly or softly reachable. + * </li> + * </ul> + * </li> + * <li> + * All references in {@code ref} are atomically cleared. + * </li> + * <li> + * All objects formerly being referenced by {@code ref} become eligible for + * finalization. + * </li> + * <li> + * At some future point, all references in {@code ref} will be enqueued + * with their corresponding reference queues, if any. + * </li> + * </ul> + * + * Weak references are useful for mappings that should have their entries + * removed automatically once they are not referenced any more (from outside). + * The difference between a {@code SoftReference} and a {@code WeakReference} is + * the point of time at which the decision is made to clear and enqueue the + * reference: + * + * <ul> + * <li> + * A {@code SoftReference} should be cleared and enqueued <em>as late as + * possible</em>, that is, in case the VM is in danger of running out of + * memory. + * </li> + * <li> + * A {@code WeakReference} may be cleared and enqueued as soon as is + * known to be weakly-referenced. + * </li> + * </ul> + * + * @since Android 1.0 + */ +public class WeakReference<T> extends Reference<T> { + + /** + * Constructs a new weak reference to the given referent. The newly created + * reference is not registered with any reference queue. + * + * @param r the referent to track + * + * @since Android 1.0 + */ + public WeakReference(T r) { + super(); + referent = r; + } + + /** + * Constructs a new weak reference to the given referent. The newly created + * reference is registered with the given reference queue. + * + * @param r the referent to track + * @param q the queue to register to the reference object with. A null value + * results in a weak reference that is not associated with any + * queue. + * + * @since Android 1.0 + */ + public WeakReference(T r, ReferenceQueue<? super T> q) { + super(); + referent = r; + queue = q; + } +} diff --git a/luni-kernel/src/main/java/java/lang/reflect/AccessibleObject.java b/luni-kernel/src/main/java/java/lang/reflect/AccessibleObject.java new file mode 100644 index 0000000..0178276 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/reflect/AccessibleObject.java @@ -0,0 +1,465 @@ +/* + * 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; +import org.apache.harmony.kernel.vm.ReflectionAccess; + +/** + * {@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 + * @see ReflectPermission + * + * @since Android 1.0 + */ +public class AccessibleObject implements AnnotatedElement { + + // If true, object is accessible, bypassing normal security checks + boolean flag = false; + + /** + * one dimensional array + */ + private static final String DIMENSION_1 = "[]"; + + /** + * two dimensional array + */ + private static final String DIMENSION_2 = "[][]"; + + /** + * three dimensional array + */ + private static final String DIMENSION_3 = "[][][]"; + + // 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. Only one security check is performed. Setting this + * flag to {@code false} will enable access checks, setting to {@code true} + * will disable them. If there is a security manager, checkPermission is + * called with a {@code ReflectPermission("suppressAccessChecks")}. + * + * @param objects + * the accessible objects + * @param flag + * the new value for the accessible flag + * + * @throws SecurityException + * if the request is denied + * + * @see #setAccessible(boolean) + * @see ReflectPermission + * + * @since Android 1.0 + */ + public static void setAccessible(AccessibleObject[] objects, boolean flag) + throws SecurityException { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkPermission(new ReflectPermission("suppressAccessChecks")); + } + + synchronized(AccessibleObject.class) { + for (int i = 0; i < objects.length; i++) { + objects[i].flag = flag; + } + } + } + + /** + * Constructs a new {@code AccessibleObject} instance. {@code + * AccessibleObject} instances can only be constructed by the virtual + * machine. + * + * @since Android 1.0 + */ + protected AccessibleObject() { + super(); + } + + /** + * Indicates whether this object is accessible without security checks being + * performed. Returns the accessible flag. + * + * @return {@code true} if this object is accessible without security + * checks, {@code false} otherwise + * + * @since Android 1.0 + */ + 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. If there is a security manager, checkPermission is called + * with a {@code ReflectPermission("suppressAccessChecks")}. + * + * @param flag + * the new value for the accessible flag + * + * @throws SecurityException + * if the request is denied + * + * @see ReflectPermission + * + * @since Android 1.0 + */ + public void setAccessible(boolean flag) throws SecurityException { + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + smgr.checkPermission(new ReflectPermission("suppressAccessChecks")); + } + + this.flag = flag; + } + + /** + * Sets the accessible flag on this instance without doing any checks. + * + * @param flag + * the new value for the accessible flag + */ + /*package*/ void setAccessibleNoCheck(boolean flag) { + this.flag = flag; + } + + public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { + return getAnnotation(annotationType) != null; + } + + public Annotation[] getDeclaredAnnotations() { + throw new RuntimeException("subclass must override this method"); + } + + public Annotation[] getAnnotations() { + // for all but Class, getAnnotations == getDeclaredAnnotations + return getDeclaredAnnotations(); + } + + /* slow, but works for all sub-classes */ + public <T extends Annotation> T getAnnotation(Class<T> annotationType) { + if (annotationType == null) { + throw new NullPointerException(); + } + Annotation[] annos = getAnnotations(); + for (int i = annos.length-1; i >= 0; --i) { + if (annos[i].annotationType() == annotationType) { + return (T) annos[i]; + } + } + return null; + } + + /** + * 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) { + result.append(types[0].getCanonicalName()); + for (int i = 1; i < types.length; i++) { + result.append(','); + result.append(types[i].getCanonicalName()); + } + } + + 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); + + /** + * Gets the unique instance of {@link ReflectionAccessImpl}. + * + * @return non-null; the unique instance + */ + static /*package*/ ReflectionAccess getReflectionAccess() { + return ReflectionAccessImpl.THE_ONE; + } + + + /** + * Appends the specified class name to the buffer. The class may represent + * a simple type, a reference type or an array type. + * + * @param sb buffer + * @param obj the class which name should be appended to the buffer + * + * @throws NullPointerException if any of the arguments is null + */ + void appendArrayType(StringBuilder sb, Class<?> obj) { + if (!obj.isArray()) { + sb.append(obj.getName()); + return; + } + int dimensions = 1; + Class simplified = obj.getComponentType(); + obj = simplified; + while (simplified.isArray()) { + obj = simplified; + dimensions++; + } + sb.append(obj.getName()); + switch (dimensions) { + case 1: + sb.append(DIMENSION_1); + break; + case 2: + sb.append(DIMENSION_2); + break; + case 3: + sb.append(DIMENSION_3); + break; + default: + for (; dimensions > 0; dimensions--) { + sb.append(DIMENSION_1); + } + } + } + + /** + * 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 sb buffer + * @param objs array of classes to print the names + * + * @throws NullPointerException if any of the arguments is null + */ + void appendArrayType(StringBuilder sb, Class[] objs) { + if (objs.length > 0) { + appendArrayType(sb, objs[0]); + for (int i = 1; i < objs.length; i++) { + sb.append(','); + appendArrayType(sb, objs[i]); + } + } + } + + /** + * 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 sb buffer + * @param objs array of classes to print the names + * + * @throws NullPointerException if any of the arguments is null + */ + void appendArrayGenericType(StringBuilder sb, Type[] objs) { + if (objs.length > 0) { + appendGenericType(sb, objs[0]); + for (int i = 1; i < objs.length; i++) { + sb.append(','); + appendGenericType(sb, objs[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()); + } + } + } + + /** + * 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. + * In case if the specified array element represents an array type its + * internal will be appended to the buffer. + * Output format: [Ljava.lang.Object;, java.io.File, void + * + * @param sb buffer + * @param objs array of classes to print the names + * + * @throws NullPointerException if any of the arguments is null + */ + void appendSimpleType(StringBuilder sb, Class<?>[] objs) { + if (objs.length > 0) { + sb.append(objs[0].getName()); + for (int i = 1; i < objs.length; i++) { + sb.append(','); + sb.append(objs[i].getName()); + } + } + } +} diff --git a/luni-kernel/src/main/java/java/lang/reflect/Array.java b/luni-kernel/src/main/java/java/lang/reflect/Array.java new file mode 100644 index 0000000..d633e69 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/reflect/Array.java @@ -0,0 +1,820 @@ +/* + * 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; + +/** + * This class provides static methods to create and access arrays dynamically. + * + * @since Android 1.0 + */ +public final class Array { + + /** + * Prevent this class from being instantiated. + */ + private Array(){ + //do nothing + } + + /** + * Returns the element of the array at the specified index. This reproduces + * the effect of {@code array[index]}. If the array component is a primitive + * type, the result is automatically wrapped. + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element, possibly wrapped + * + * @throws NullPointerException + * if the array is null + * @throws IllegalArgumentException + * if {@code array} is not an array + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static Object get(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof Object[]) + return ((Object[]) array)[index]; + + if (array instanceof boolean[]) + return ((boolean[]) array)[index] ? Boolean.TRUE : Boolean.FALSE; + + if (array instanceof byte[]) + return new Byte(((byte[]) array)[index]); + + if (array instanceof char[]) + return new Character(((char[]) array)[index]); + + if (array instanceof short[]) + return new Short(((short[]) array)[index]); + + if (array instanceof int[]) + return new Integer(((int[]) array)[index]); + + if (array instanceof long[]) + return new Long(((long[]) array)[index]); + + if (array instanceof float[]) + return new Float(((float[]) array)[index]); + + if (array instanceof double[]) + return new Double(((double[]) array)[index]); + + if (array == null) + throw new NullPointerException(); + + throw new IllegalArgumentException("Not an array"); + } + + /** + * Returns the element of the array at the specified index, converted to a + * {@code boolean}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static boolean getBoolean(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof boolean[]) { + return ((boolean[]) array)[index]; + } else if (array == null) { + throw new NullPointerException(); + } else if (array.getClass().isArray()) { + throw new IllegalArgumentException("Wrong array type"); + } else { + throw new IllegalArgumentException("Not an array"); + } + } + + /** + * Returns the element of the array at the specified index, converted to a + * {@code byte}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static byte getByte(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof byte[]) { + return ((byte[]) array)[index]; + } else { + return getBoolean(array, index) ? (byte)1 : (byte)0; + } + } + + /** + * Returns the element of the array at the specified index, converted to a + * {@code char}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static char getChar(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof char[]) { + return ((char[]) array)[index]; + } else if (array == null) { + throw new NullPointerException(); + } else if (array.getClass().isArray()) { + throw new IllegalArgumentException("Wrong array type"); + } else { + throw new IllegalArgumentException("Not an array"); + } + } + + /** + * Returns the element of the array at the specified index, converted to a + * {@code double}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static double getDouble(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof double[]) { + return ((double[]) array)[index]; + } else { + return getFloat(array, index); + } + } + + /** + * Returns the element of the array at the specified index, converted to a + * {@code float}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static float getFloat(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof float[]) { + return ((float[]) array)[index]; + } else { + return getLong(array, index); + } + } + + /** + * Returns the element of the array at the specified index, converted to an + * {@code int}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static int getInt(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof int[]) { + return ((int[]) array)[index]; + } else { + return getShort(array, index); + } + } + + /** + * Returns the length of the array. This reproduces the effect of {@code + * array.length} + * + * @param array + * the array + * + * @return the length of the array + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array + * + * @since Android 1.0 + */ + public static int getLength(Object array) { + if (array instanceof Object[]) + return ((Object[]) array).length; + + if (array instanceof boolean[]) + return ((boolean[]) array).length; + + if (array instanceof byte[]) + return ((byte[]) array).length; + + if (array instanceof char[]) + return ((char[]) array).length; + + if (array instanceof short[]) + return ((short[]) array).length; + + if (array instanceof int[]) + return ((int[]) array).length; + + if (array instanceof long[]) + return ((long[]) array).length; + + if (array instanceof float[]) + return ((float[]) array).length; + + if (array instanceof double[]) + return ((double[]) array).length; + + if (array == null) + throw new NullPointerException(); + + throw new IllegalArgumentException("Not an array"); + } + + /** + * Returns the element of the array at the specified index, converted to a + * {@code long}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static long getLong(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof long[]) { + return ((long[]) array)[index]; + } else { + return getInt(array, index); + } + } + + /** + * Returns the element of the array at the specified index, converted to a + * {@code short}, if possible. This reproduces the effect of {@code + * array[index]} + * + * @param array + * the array + * @param index + * the index + * + * @return the requested element + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the element at the + * index position can not be converted to the return type + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static short getShort(Object array, int index) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof short[]) + return ((short[]) array)[index]; + + return getByte(array, index); + } + + /** + * Returns a new multidimensional array of the specified component type and + * dimensions. This reproduces the effect of {@code new + * componentType[d0][d1]...[dn]} for a dimensions array of { d0, d1, ... , + * dn }. + * + * @param componentType + * the component type of the new array + * @param dimensions + * the dimensions of the new array + * + * @return the new array + * + * @throws NullPointerException + * if the component type is {@code null} + * @throws NegativeArraySizeException + * if any of the dimensions are negative + * @throws IllegalArgumentException + * if the array of dimensions is of size zero, or exceeds the + * limit of the number of dimension for an array (currently 255) + * + * @since Android 1.0 + */ + public static Object newInstance(Class<?> componentType, int[] dimensions) + throws NegativeArraySizeException, IllegalArgumentException { + if (dimensions.length <= 0 || dimensions.length > 255) + throw new IllegalArgumentException("Bad number of dimensions"); + + if (componentType == Void.TYPE) + throw new IllegalArgumentException(); + + if (componentType == null) + throw new NullPointerException(); + + return createMultiArray(componentType, dimensions); + } + + /* + * Create a multi-dimensional array of objects with the specified type. + */ + native private static Object createMultiArray(Class<?> componentType, + int[] dimensions) throws NegativeArraySizeException; + + /** + * Returns a new array of the specified component type and length. This + * reproduces the effect of {@code new componentType[size]}. + * + * @param componentType + * the component type of the new array + * @param size + * the length of the new array + * + * @return the new array + * + * @throws NullPointerException + * if the component type is null + * @throws NegativeArraySizeException + * if {@code size < 0} + * + * @since Android 1.0 + */ + public static Object newInstance(Class<?> componentType, int size) + throws NegativeArraySizeException { + if (!componentType.isPrimitive()) + return createObjectArray(componentType, size); + + if (componentType == Boolean.TYPE) + return new boolean[size]; + + if (componentType == Byte.TYPE) + return new byte[size]; + + if (componentType == Character.TYPE) + return new char[size]; + + if (componentType == Short.TYPE) + return new short[size]; + + if (componentType == Integer.TYPE) + return new int[size]; + + if (componentType == Long.TYPE) + return new long[size]; + + if (componentType == Float.TYPE) + return new float[size]; + + if (componentType == Double.TYPE) + return new double[size]; + + if (componentType == Void.TYPE) + throw new IllegalArgumentException(); + + throw new RuntimeException(); // should be impossible + } + + /* + * Create a one-dimensional array of objects with the specified type. + */ + native private static Object createObjectArray(Class<?> componentType, + int length) throws NegativeArraySizeException; + + /** + * Sets the element of the array at the specified index to the value. This + * reproduces the effect of {@code array[index] = value}. If the array + * component is a primitive type, the value is automatically unwrapped. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void set(Object array, int index, Object value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (!array.getClass().isArray()) { + throw new IllegalArgumentException("Not an array type"); + } + + if (array instanceof Object[]) { + if (value != null && + !array.getClass().getComponentType().isInstance(value)) { + // incompatible object type for this array + throw new IllegalArgumentException("Wrong array type"); + } + + ((Object[]) array)[index] = value; + } else { + if (value == null) { + throw new IllegalArgumentException("Primitive array can't take null values."); + } + + if (value instanceof Boolean) + setBoolean(array, index, ((Boolean) value).booleanValue()); + else if (value instanceof Byte) + setByte(array, index, ((Byte) value).byteValue()); + else if (value instanceof Character) + setChar(array, index, ((Character) value).charValue()); + else if (value instanceof Short) + setShort(array, index, ((Short) value).shortValue()); + else if (value instanceof Integer) + setInt(array, index, ((Integer) value).intValue()); + else if (value instanceof Long) + setLong(array, index, ((Long) value).longValue()); + else if (value instanceof Float) + setFloat(array, index, ((Float) value).floatValue()); + else if (value instanceof Double) + setDouble(array, index, ((Double) value).doubleValue()); + } + } + + /** + * Sets the element of the array at the specified index to the {@code + * boolean} value. This reproduces the effect of {@code array[index] = + * value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setBoolean(Object array, int index, boolean value) { + if (array instanceof boolean[]) { + ((boolean[]) array)[index] = value; + } else { + setByte(array, index, value ? (byte)1 : (byte)0); + } + } + + /** + * Sets the element of the array at the specified index to the {@code byte} + * value. This reproduces the effect of {@code array[index] = value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setByte(Object array, int index, byte value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof byte[]) { + ((byte[]) array)[index] = value; + } else { + setShort(array, index, value); + } + } + + /** + * Set the element of the array at the specified index to the {@code char} + * value. This reproduces the effect of {@code array[index] = value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setChar(Object array, int index, char value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof char[]) { + ((char[]) array)[index] = value; + } else if (array == null) { + throw new NullPointerException(); + } else if (!array.getClass().isArray()) { + throw new IllegalArgumentException("Not an array"); + } else { + throw new IllegalArgumentException("Wrong array type"); + } + } + + /** + * Set the element of the array at the specified index to the {@code double} + * value. This reproduces the effect of {@code array[index] = value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setDouble(Object array, int index, double value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof double[]) { + ((double[]) array)[index] = value; + } else if (array == null) { + throw new NullPointerException(); + } else if (!array.getClass().isArray()) { + throw new IllegalArgumentException("Not an array"); + } else { + throw new IllegalArgumentException("Wrong array type"); + } + } + + /** + * Set the element of the array at the specified index to the {@code float} + * value. This reproduces the effect of {@code array[index] = value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setFloat(Object array, int index, float value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof float[]) { + ((float[]) array)[index] = value; + } else { + setDouble(array, index, value); + } + } + + /** + * Set the element of the array at the specified index to the {@code int} + * value. This reproduces the effect of {@code array[index] = value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setInt(Object array, int index, int value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof int[]) { + ((int[]) array)[index] = value; + } else { + setLong(array, index, value); + } + } + + /** + * Set the element of the array at the specified index to the {@code long} + * value. This reproduces the effect of {@code array[index] = value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setLong(Object array, int index, long value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof long[]) { + ((long[]) array)[index] = value; + } else { + setFloat(array, index, value); + } + } + + /** + * Set the element of the array at the specified index to the {@code short} + * value. This reproduces the effect of {@code array[index] = value}. + * + * @param array + * the array + * @param index + * the index + * @param value + * the new value + * + * @throws NullPointerException + * if the {@code array} is {@code null} + * @throws IllegalArgumentException + * if the {@code array} is not an array or the value cannot be + * converted to the array type by a widening conversion + * @throws ArrayIndexOutOfBoundsException + * if {@code index < 0 || index >= array.length} + * + * @since Android 1.0 + */ + public static void setShort(Object array, int index, short value) + throws IllegalArgumentException, ArrayIndexOutOfBoundsException { + if (array instanceof short[]) { + ((short[]) array)[index] = value; + } else { + setInt(array, index, value); + } + } + +} diff --git a/luni-kernel/src/main/java/java/lang/reflect/Constructor.java b/luni-kernel/src/main/java/java/lang/reflect/Constructor.java new file mode 100644 index 0000000..c6927eb --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/reflect/Constructor.java @@ -0,0 +1,491 @@ +/* + * 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 dalvik.system.VMStack; + +import java.lang.annotation.Annotation; + +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 + * + * @since Android 1.0 + */ +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( + VMStack.getCallingClassLoader2()); + parser.parseForConstructor(this, signatureAttribute); + 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 = getSignatureAnnotation(declaringClass, slot); + + if (annotation == null) { + return null; + } + + return StringUtils.combineStrings(annotation); + } + + /** + * Get the Signature annotation for this constructor. Returns null if not + * found. + */ + native private Object[] getSignatureAnnotation(Class declaringClass, + int slot); + + 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 + * + * @since Android 1.0 + */ + 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 + appendArrayType(sb, getDeclaringClass()); + // append parameters + sb.append('('); + appendArrayGenericType(sb, + Types.getClonedTypeArray(genericParameterTypes)); + sb.append(')'); + // append exeptions if any + Type[] genericEceptionTypeArray = + Types.getClonedTypeArray(genericExceptionTypes); + if (genericEceptionTypeArray.length > 0) { + sb.append(" throws "); + appendArrayGenericType(sb, genericEceptionTypeArray); + } + 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 + * + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + public Type[] getGenericExceptionTypes() { + initGenericTypes(); + return Types.getClonedTypeArray(genericExceptionTypes); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return getDeclaredAnnotations(declaringClass, slot); + } + native private Annotation[] getDeclaredAnnotations(Class declaringClass, + int slot); + + /** + * 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 + * + * @since Android 1.0 + */ + public Annotation[][] getParameterAnnotations() { + Annotation[][] parameterAnnotations + = getParameterAnnotations(declaringClass, slot); + if (parameterAnnotations.length == 0) { + return Method.noAnnotations(parameterTypes.length); + } + return parameterAnnotations; + } + native private Annotation[][] getParameterAnnotations(Class declaringClass, + int slot); + + /** + * Indicates whether or not this constructor takes a variable number of + * arguments. + * + * @return {@code true} if a vararg is declare, otherwise + * {@code false} + * + * @since Android 1.0 + */ + public boolean isVarArgs() { + int mods = getConstructorModifiers(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 + * + * @since Android 1.0 + */ + public boolean isSynthetic() { + int mods = getConstructorModifiers(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 + * + * @since Android 1.0 + */ + @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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public Class<?>[] getExceptionTypes() { + if (exceptionTypes == null) + return new Class[0]; + return exceptionTypes; + } + + /** + * 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 + * + * @since Android 1.0 + */ + public int getModifiers() { + return getConstructorModifiers(declaringClass, slot); + } + + private native int getConstructorModifiers(Class<T> declaringClass, int slot); + + /** + * Returns the name of this constructor. + * + * @return the name of this constructor + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public Class<?>[] getParameterTypes() { + return parameterTypes; + } + + /** + * 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 + * + * @since Android 1.0 + */ + @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 unwrapped. If the unwrapping 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + @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/luni-kernel/src/main/java/java/lang/reflect/Field.java b/luni-kernel/src/main/java/java/lang/reflect/Field.java new file mode 100644 index 0000000..0ea16df --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/reflect/Field.java @@ -0,0 +1,939 @@ +/* + * 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 dalvik.system.VMStack; + +import java.lang.annotation.Annotation; + +import org.apache.harmony.luni.lang.reflect.GenericSignatureParser; +import org.apache.harmony.luni.lang.reflect.Types; +import org.apache.harmony.kernel.vm.StringUtils; + +/** + * This class represents a field. Information about the field can be accessed, + * and the field's value can be accessed dynamically. + * + * @since Android 1.0 + */ +public final class Field extends AccessibleObject implements Member { + + private Class<?> declaringClass; + + private Class<?> type; + + private Type genericType; + + private volatile boolean genericTypesAreInitialized = false; + + private String name; + + private int slot; + + private static final int TYPE_BOOLEAN = 1; + + private static final int TYPE_BYTE = 2; + + private static final int TYPE_CHAR = 3; + + private static final int TYPE_SHORT = 4; + + private static final int TYPE_INTEGER = 5; + + private static final int TYPE_FLOAT = 6; + + private static final int TYPE_LONG = 7; + + private static final int TYPE_DOUBLE = 8; + + /** + * 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( + VMStack.getCallingClassLoader2()); + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + public Type getGenericType() { + initGenericType(); + return Types.getType(genericType); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return getDeclaredAnnotations(declaringClass, slot); + } + + native private Annotation[] getDeclaredAnnotations(Class declaringClass, int slot); + + /** + * 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 + * @since Android 1.0 + */ + @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 wrapped. + * <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. + * <p> + * + * @param object + * the object to access + * @return the field value, possibly wrapped + * @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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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. + * @since Android 1.0 + */ + @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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + @Override + public int hashCode() { + // BEGIN android-changed + return name.hashCode() ^ getDeclaringClass().getName().hashCode(); + // END android-changed + } + + /** + * 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 + * unwrapped. If the unwrap 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + 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 + * @since Android 1.0 + */ + @Override + public String toString() { + StringBuffer result = new StringBuffer(Modifier.toString(getModifiers())); + + if (result.length() != 0) + result.append(' '); + result.append(type.getName()); + result.append(' '); + result.append(declaringClass.getName()); + 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, int type_no) throws IllegalAccessException; + + private native int getIField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no) throws IllegalAccessException; + + private native long getJField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no) throws IllegalAccessException; + + private native boolean getZField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no) throws IllegalAccessException; + + private native float getFField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no) throws IllegalAccessException; + + private native char getCField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no) throws IllegalAccessException; + + private native short getSField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no) throws IllegalAccessException; + + private native byte getBField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no) 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, int type_no, double v) throws IllegalAccessException; + + private native void setIField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no, int i) throws IllegalAccessException; + + private native void setJField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no, long j) throws IllegalAccessException; + + private native void setZField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no, boolean z) throws IllegalAccessException; + + private native void setFField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no, float f) throws IllegalAccessException; + + private native void setCField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no, char c) throws IllegalAccessException; + + private native void setSField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no, short s) throws IllegalAccessException; + + private native void setBField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, int type_no, byte b) throws IllegalAccessException; + +} diff --git a/luni-kernel/src/main/java/java/lang/reflect/Method.java b/luni-kernel/src/main/java/java/lang/reflect/Method.java new file mode 100644 index 0000000..473726d --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/reflect/Method.java @@ -0,0 +1,595 @@ +/* + * 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 dalvik.system.VMStack; + +import java.lang.annotation.Annotation; + +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. + * + * @since Android 1.0 + */ +public final class Method extends AccessibleObject implements GenericDeclaration, Member { + + 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( + VMStack.getCallingClassLoader2()); + parser.parseForMethod(this, signatureAttribute); + 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. + */ + native private 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 + * + * @since Android 1.0 + */ + 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 + appendArrayType(sb, getDeclaringClass()); + sb.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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public Type getGenericReturnType() { + initGenericTypes(); + return Types.getType(genericReturnType); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return getDeclaredAnnotations(declaringClass, slot); + } + native private Annotation[] getDeclaredAnnotations(Class declaringClass, + int slot); + + 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 + * + * @since Android 1.0 + */ + public Annotation[][] getParameterAnnotations() { + Annotation[][] parameterAnnotations + = getParameterAnnotations(declaringClass, slot); + if (parameterAnnotations.length == 0) { + return noAnnotations(parameterTypes.length); + } + return parameterAnnotations; + } + + native private 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + @Override + public boolean equals(Object object) { + return object instanceof Method && toString().equals(object.toString()); + } + + /** + * Returns the class that declares this method. + * + * @return the declaring class + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public Class<?>[] getExceptionTypes() { + if (exceptionTypes == null) { + return new Class[0]; + } + + return exceptionTypes; + } + + /** + * 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 + * + * @since Android 1.0 + */ + public int getModifiers() { + return getMethodModifiers(declaringClass, slot); + } + + private native int getMethodModifiers(Class<?> decl_class, int slot); + + /** + * Returns the name of the method represented by this {@code Method} + * instance. + * + * @return the name of this method + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + public Class<?>[] getParameterTypes() { + return parameterTypes; + } + + /** + * Returns the {@code Class} associated with the return type of this + * method. + * + * @return the return type + * + * @since Android 1.0 + */ + 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 + * + * @since Android 1.0 + */ + @Override + public int hashCode() { + return name.hashCode(); + } + + /** + * Returns the result of dynamically invoking this method. This reproduces + * the effect of {@code receiver.methodName(arg1, arg2, ... , argN)} This + * method performs the following: + * <ul> + * <li>If this method is static, the receiver argument is ignored.</li> + * <li>Otherwise, if the receiver is null, a NullPointerException is thrown. + * </li> + * <li>If the receiver is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown.</li> + * <li>If this Method object is enforcing access control (see + * AccessibleObject) and this method 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 unwrapped. If the unwrapping 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>If this method is static, it is invoked directly. If it is + * non-static, this method and the receiver are then used to perform a + * standard dynamic method lookup. The resulting method is then invoked.</li> + * <li>If an exception is thrown during the invocation it is caught and + * wrapped in an InvocationTargetException. This exception is then thrown.</li> + * <li>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 first wrapped. If the return type is void, null is + * returned.</li> + * </ul> + * + * @param receiver + * the object on which to call this method + * @param args + * the arguments to the method + * + * @return the new, initialized, object + * + * @throws NullPointerException + * if the receiver is null for a non-static method + * @throws IllegalAccessException + * if this method is not accessible + * @throws IllegalArgumentException + * if an incorrect number of arguments are passed, the receiver + * is incompatible with the declaring class, or an argument + * could not be converted by a widening conversion + * @throws InvocationTargetException + * if an exception was thrown by the invoked method + * + * @see AccessibleObject + * + * @since Android 1.0 + */ + public Object invoke(Object receiver, Object... args) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException { + if (args == null) { + args = new Object[0]; + } + + 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 + * + * @since Android 1.0 + */ + @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/luni-kernel/src/main/java/java/lang/reflect/ReflectionAccessImpl.java b/luni-kernel/src/main/java/java/lang/reflect/ReflectionAccessImpl.java new file mode 100644 index 0000000..15cd798 --- /dev/null +++ b/luni-kernel/src/main/java/java/lang/reflect/ReflectionAccessImpl.java @@ -0,0 +1,54 @@ +/* + * 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 org.apache.harmony.kernel.vm.ReflectionAccess; + +/** + * Implementation of bridge from {@code java.lang} to + * {@code java.lang.reflect}. + */ +/*package*/ final class ReflectionAccessImpl implements ReflectionAccess { + /** non-null; unique instance of this class */ + /*package*/ static final ReflectionAccessImpl THE_ONE = + new ReflectionAccessImpl(); + + /** + * This class is not publicly instantiable. Use {@link #THE_ONE}. + */ + private ReflectionAccessImpl() { + // This space intentionally left blank. + } + + public Method clone(Method method) { + return new Method(method); + } + + public Field clone(Field field) { + return new Field(field); + } + + public Method accessibleClone(Method method) { + Method result = new Method(method); + result.setAccessibleNoCheck(true); + return result; + } + + public void setAccessibleNoCheck(AccessibleObject ao, boolean accessible) { + ao.setAccessibleNoCheck(accessible); + } +} diff --git a/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/LangAccess.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/LangAccess.java new file mode 100644 index 0000000..8b321e4 --- /dev/null +++ b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/LangAccess.java @@ -0,0 +1,129 @@ +/* + * 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; + +import dalvik.system.VMStack; + +/** + * Bridge into <code>java.lang</code> from other trusted parts of the + * core library. Trusted packages either get seeded with an instance + * of this class directly or may call {@link #getInstance} on this + * class, to allow them to call into what would otherwise be + * package-scope functionality in <code>java.lang</code>. + */ +public abstract class LangAccess { + /** unique instance of this class */ + private static LangAccess theInstance = null; + + /** + * Sets the unique instance of this class. This may only be done once. + * + * @param instance non-null; the instance + */ + public static void setInstance(LangAccess instance) { + if (theInstance != null) { + throw new UnsupportedOperationException("already initialized"); + } + + theInstance = instance; + } + + /** + * Gets the unique instance of this class. This is only allowed in + * very limited situations. + */ + public static LangAccess getInstance() { + /* + * Only code on the bootclasspath is allowed to get at the + * instance. + */ + ClassLoader calling = VMStack.getCallingClassLoader2(); + ClassLoader current = LangAccess.class.getClassLoader(); + + if ((calling != null) && (calling != current)) { + throw new SecurityException("LangAccess access denied"); + } + + if (theInstance == null) { + throw new UnsupportedOperationException("not yet initialized"); + } + + return theInstance; + } + + /** + * Gets a shared array of the enum constants of a given class in + * declaration (ordinal) order. It is not safe to hand out this + * array to any user code. + * + * @param clazz non-null; the class in question + * @return null-ok; the class's list of enumerated constants in + * declaration order or <code>null</code> if the given class is + * not an enumeration + */ + public abstract <T> T[] getEnumValuesInOrder(Class<T> clazz); + + /** + * Unparks the given 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> + * + * @param thread non-null; the thread to unpark + */ + public abstract void unpark(Thread thread); + + /** + * 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> + * + * @param nanos number of nanoseconds to park for or <code>0</code> + * to park indefinitely + * @throws IllegalArgumentException thrown if <code>nanos < 0</code> + */ + public abstract void parkFor(long nanos); + + /** + * 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> + * + * @param time the time after which the thread should be unparked, + * in absolute milliseconds-since-the-epoch + */ + public abstract void parkUntil(long time); +} diff --git a/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/ReflectionAccess.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/ReflectionAccess.java new file mode 100644 index 0000000..1487ba1 --- /dev/null +++ b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/ReflectionAccess.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; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Bridge from <code>java.lang</code> to <code>java.lang.reflect</code>. + * The package <code>java.lang</code> gets seeded with an instance of + * this interface, to allow it to call into what would otherwise be + * package-scope functionality in <code>java.lang.reflect</code>. + */ +public interface ReflectionAccess { + /** + * Gets a clone of the given method. + * + * @param method non-null; the method to clone + * @return non-null; the clone + */ + public Method clone(Method method); + + /** + * Gets a clone of the given field. + * + * @param field non-null; the field to clone + * @return non-null; the clone + */ + public Field clone(Field field); + + /** + * Gets a clone of the given method, where the clone has + * its "accessible" flag set to <code>true</code> + * + * @param method non-null; the method to clone + * @return non-null; the accessible clone + */ + public Method accessibleClone(Method method); + + /** + * Sets the accessible flag on a given {@link AccessibleObject} + * without doing any checks. + * + * @param ao non-null; the instance in question + * @param flag the new value for the accessible flag + */ + public void setAccessibleNoCheck(AccessibleObject ao, boolean flag); +} diff --git a/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java new file mode 100644 index 0000000..b392388 --- /dev/null +++ b/luni-kernel/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/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/VM.java b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/VM.java new file mode 100644 index 0000000..8aa7c4b --- /dev/null +++ b/luni-kernel/src/main/java/org/apache/harmony/kernel/vm/VM.java @@ -0,0 +1,289 @@ +/* + * 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.kernel.vm; + +/** + * This class must be implemented by the vm vendor. Represents the running + * virtual machine. All VM specific API are implemented on this class. + * <p> + * Note that all methods in VM are static. There is no singleton instance which + * represents the actively running VM. + */ +public final class VM { + + /* + * kernelVersion has the format: aabbxxyy where: aa - major version of + * kernel. Must equal that stored in jcl. bb - minor version of kernel. Must + * be >= that in jcl. xx - major version of jcl. Must equal that stored in + * kernel. yy - minor version of jcl. Must be >= that in kernel. + */ + private static final int kernelVersion = 0x01000100; + + /** + * This method must be provided by the vm vendor, as it is used by + * org.apache.harmony.kernel.vm.MsgHelp.setLocale() to get the bootstrap + * ClassLoader. MsgHelp uses the bootstrap ClassLoader to find the resource + * bundle of messages packaged with the bootstrap classes. Returns the + * ClassLoader of the method (including natives) at the specified depth on + * the stack of the calling thread. Frames representing the VM + * implementation of java.lang.reflect are not included in the list. This is + * not a public method as it can return the bootstrap class loader, which + * should not be accessed by non-bootstrap classes. Notes: + * <ul> + * <li>This method operates on the defining classes of methods on stack. + * NOT the classes of receivers.</li> + * <li>The item at depth zero is the caller of this method</li> + * </ul> + * + * @param depth the stack depth of the requested ClassLoader + * @return the ClassLoader at the specified depth + * @see java.lang.ClassLoader#getStackClassLoader + */ + static final ClassLoader getStackClassLoader(int depth) { + return null; + }; + + /** + * This method must be provided by the vm vendor, as it is used by other + * provided class implementations. For example, + * java.io.ObjectInputStream.readObject() and + * java.io.ObjectInputStream.resolveProxyClass(). It is also useful for + * other classes, such as java.rmi.server.RMIClassLoader. Walk the stack and + * answer the most recent non-null and non-bootstrap ClassLoader on the + * stack of the calling thread. If no such ClassLoader is found, null is + * returned. Notes: 1) This method operates on the defining classes of + * methods on stack. NOT the classes of receivers. + * + * @return the first non-bootstrap ClassLoader on the stack + */ + static public final ClassLoader getNonBootstrapClassLoader() { + return null; + }; + + /** + * Initialize the classloader. + * + * @param loader ClassLoader the ClassLoader instance + * @param bootLoader boolean true for the bootstrap class loader + */ + public final static void initializeClassLoader(ClassLoader loader, boolean bootLoader) { + return; + }; + + /** + * This method must be provided by the vm vendor. + * + * Searches an internal table of strings for a string equal to the specified + * String. If the string is not in the table, it is added. Returns the + * string contained in the table which is equal to the specified String. The + * same string object is always answered for strings which are equal. + * + * @param string the String to intern + * + * @return the interned string equal to the specified String + */ + public static final String intern(String string) { + return null; + } + + /** + * Native used to find and load a class using the VM + * + * @return java.lang.Class the class or null. + * @param className String the name of the class to search for. + * @param classLoader the classloader to do the work + */ + static Class<?> findClassOrNull(String className, ClassLoader classLoader) { + return null; + } + + /** + * This method must be included, as it is used by + * ResourceBundle.getBundle(), and other places as well. The reference + * implementation of this method uses the getStackClassLoader() method. + * Returns the ClassLoader of the method that called the caller. i.e. A.x() + * calls B.y() calls callerClassLoader(), A's ClassLoader will be returned. + * Returns null for the bootstrap ClassLoader. + * + * @return a ClassLoader or null for the bootstrap ClassLoader + * @throws SecurityException when called from a non-bootstrap Class + */ + public static ClassLoader callerClassLoader() { + return null; + } + + /** + * This method must be provided by the vm vendor, as it is used by + * org.apache.harmony.luni.util.MsgHelp.setLocale() to get the bootstrap + * ClassLoader. MsgHelp uses the bootstrap ClassLoader to find the resource + * bundle of messages packaged with the bootstrap classes. The reference + * implementation of this method uses the getStackClassLoader() method. + * + * Returns the ClassLoader of the method that called the caller. i.e. A.x() + * calls B.y() calls callerClassLoader(), A's ClassLoader will be returned. + * Returns null for the bootstrap ClassLoader. + * + * @return a ClassLoader + * + * @throws SecurityException when called from a non-bootstrap Class + */ + public static ClassLoader bootCallerClassLoader() { + return null; + } + + /** + * Native used to dump a string to the system console for debugging. + * + * @param str String the String to display + */ + public static void dumpString(String str) { + return; + } + + /** + * Get the classpath entry that was used to load the class that is the arg. + * <p> + * This method is for internal use only. + * + * @param targetClass Class the class to set the classpath of. + * @see java.lang.Class + */ + static int getCPIndexImpl(Class<?> targetClass) { + return 0; + } + + /** + * Does internal initialization required by VM. + * + */ + static void initializeVM() { + } + + /** + * Registers a new virtual-machine shutdown hook. This is equivalent to the + * 1.3 API of the same name. + * + * @param hook the hook (a Thread) to register + */ + public static void addShutdownHook(Thread hook) { + return; + } + + /** + * De-registers a previously-registered virtual-machine shutdown hook. This + * is equivalent to the 1.3 API of the same name. + * + * @param hook the hook (a Thread) to de-register + * @return true if the hook could be de-registered + */ + public static boolean removeShutdownHook(Thread hook) { + return false; + } + + /** + * This method must be provided by the vm vendor. Called to signal that the + * org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection + * class has been loaded and JarURLConnection.closeCachedFiles() should be + * called on VM shutdown. + */ + public static void closeJars() { + return; + } + + /** + * This method must be provided by the vm vendor. Called to signal that the + * org.apache.harmony.luni.util.DeleteOnExit class has been loaded and + * DeleteOnExit.deleteOnExit() should be called on VM shutdown. + */ + public static void deleteOnExit() { + return; + } + + // Constants used by getClassPathEntryType to indicate the class path entry + // type + static final int CPE_TYPE_UNKNOWN = 0; + + static final int CPE_TYPE_DIRECTORY = 1; + + static final int CPE_TYPE_JAR = 2; + + static final int CPE_TYPE_TCP = 3; + + static final int CPE_TYPE_UNUSABLE = 5; + + /** + * Return the type of the specified entry on the class path for a + * ClassLoader. Valid types are: CPE_TYPE_UNKNOWN CPE_TYPE_DIRECTORY + * CPE_TYPE_JAR CPE_TYPE_TCP - this is obsolete CPE_TYPE_UNUSABLE + * + * @param classLoader the ClassLoader + * @param cpIndex the index on the class path + * + * @return a int which specifies the class path entry type + */ + static final int getClassPathEntryType(Object classLoader, int cpIndex) { + return 0; + } + + /** + * Returns command line arguments passed to the VM. Internally these are + * broken into optionString and extraInfo. This only returns the + * optionString part. + * <p> + * + * @return a String array containing the optionString part of command line + * arguments + */ + public static String[] getVMArgs() { + return null; + } + + /** + * Return the number of entries on the bootclasspath. + * + * @return an int which is the number of entries on the bootclasspath + */ + static int getClassPathCount() { + return 0; + } + + /** + * Return the specified bootclasspath entry. + * + * @param index the index of the bootclasspath entry + * + * @return a byte array containing the bootclasspath entry + * specified in the vm options + */ + static byte[] getPathFromClassPath(int index) { + return null; + } + + /** + * This method must be provided by the vm vendor. + * + * Returns an int containing the version number of the kernel. Used to check for kernel + * compatibility. + * + * @return an int containing the kernel version number + */ + public static int getKernelVersion() { + return kernelVersion; + } + +} diff --git a/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java b/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java new file mode 100644 index 0000000..a898f54 --- /dev/null +++ b/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java @@ -0,0 +1,323 @@ +/* + * 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.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +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()) { + AccessController.doPrivileged(new PrivilegedAction<Object>(){ + public Object run() { + try { + el.definingMethod.setAccessible(true); + } catch (Exception ignore) {} + return null; + } + }); + } + 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() { + String res = "@" + klazz.getName() + "("; + for(int i = 0; i < elements.length; i++) { + if ( i != 0 ) { + res += ", "; + } + res += elements[i].toString();; + } + return res + ")"; + } + + /** + * 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/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java b/luni-kernel/src/main/java/org/apache/harmony/lang/annotation/AnnotationMember.java new file mode 100644 index 0000000..f8dea31 --- /dev/null +++ b/luni-kernel/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/luni-kernel/src/main/native/java_lang_ProcessManager.c b/luni-kernel/src/main/native/java_lang_ProcessManager.c new file mode 100644 index 0000000..ee2fc58 --- /dev/null +++ b/luni-kernel/src/main/native/java_lang_ProcessManager.c @@ -0,0 +1,438 @@ +/* + * 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. + */ + +#define LOG_TAG "ProcessManager" + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "utils/Log.h" +#include "AndroidSystemNatives.h" + +/** Environment variables. */ +extern char **environ; + +static jmethodID onExitMethod = NULL; +static jfieldID descriptorField = NULL; + +#ifdef ANDROID +// Keeps track of the system properties fd so we don't close it. +static int androidSystemPropertiesFd = -1; +#endif + +/* + * These are constants shared with the higher level code in + * ProcessManager.java. + */ +#define WAIT_STATUS_UNKNOWN (-1) // unknown child status +#define WAIT_STATUS_NO_CHILDREN (-2) // no children to wait for +#define WAIT_STATUS_STRANGE_ERRNO (-3) // observed an undocumented errno + +/** Closes a file descriptor. */ +static int closeNow(int fd) { + int result; + do { + result = close(fd); + } while (result == -1 && errno == EINTR); + return result; +} + +/** Closes a file descriptor. */ +static void java_lang_ProcessManager_close(JNIEnv* env, + jclass clazz, jobject javaDescriptor) { + int fd = (*env)->GetIntField(env, javaDescriptor, descriptorField); + if (closeNow(fd) == -1) { + jclass ioException = (*env)->FindClass(env, "java/io/IOException"); + (*env)->ThrowNew(env, ioException, strerror(errno)); + } +} + +/** + * Kills process with the given ID. + */ +static void java_lang_ProcessManager_kill(JNIEnv* env, jclass clazz, jint pid) { + int result = kill((pid_t) pid, SIGKILL); + if (result == -1) { + jniThrowIOException(env, errno); + } +} + +/** + * Loops indefinitely and calls ProcessManager.onExit() when children exit. + */ +static void java_lang_ProcessManager_watchChildren(JNIEnv* env, jobject o) { + if (onExitMethod == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "staticInitialize() must run first."); + } + + while (1) { + int status; + + pid_t pid = wait(&status); + + if (pid >= 0) { + // Extract real status. + if (WIFEXITED(status)) { + status = WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + status = WTERMSIG(status); + } else if (WIFSTOPPED(status)) { + status = WSTOPSIG(status); + } else { + status = WAIT_STATUS_UNKNOWN; + } + } else { + /* + * The pid should be -1 already, but force it here just in case + * we somehow end up with some other negative value. + */ + pid = -1; + + switch (errno) { + case ECHILD: { + /* + * Expected errno: There are no children to wait() + * for. The callback will sleep until it is + * informed of another child coming to life. + */ + status = WAIT_STATUS_NO_CHILDREN; + break; + } + case EINTR: { + /* + * An unblocked signal came in while waiting; just + * retry the wait(). + */ + continue; + } + default: { + /* + * Unexpected errno, so squawk! Note: Per the + * Linux docs, there are no errnos defined for + * wait() other than the two that are handled + * immediately above. + */ + LOGE("Error %d calling wait(): %s", errno, + strerror(errno)); + status = WAIT_STATUS_STRANGE_ERRNO; + break; + } + } + } + + (*env)->CallVoidMethod(env, o, onExitMethod, pid, status); + + if ((*env)->ExceptionOccurred(env)) { + /* + * The callback threw, so break out of the loop and return, + * letting the exception percolate up. + */ + break; + } + } +} + +/** Close all open fds > 2 (i.e. everything but stdin/out/err). */ +static void closeNonStandardFds(int skipFd) { + DIR* dir = opendir("/proc/self/fd"); + + if (dir == NULL) { + // Print message to standard err. The parent process can read this + // from Process.getErrorStream(). + perror("opendir"); + return; + } + + struct dirent* entry; + int dirFd = dirfd(dir); + while ((entry = readdir(dir)) != NULL) { + int fd = atoi(entry->d_name); + if (fd > 2 && fd != dirFd && fd != skipFd +#ifdef ANDROID + && fd != androidSystemPropertiesFd +#endif + ) { + close(fd); + } + } + + closedir(dir); +} + +#define PIPE_COUNT (4) // number of pipes used to communicate with child proc + +/** Closes all pipes in the given array. */ +static void closePipes(int pipes[], int skipFd) { + int i; + for (i = 0; i < PIPE_COUNT * 2; i++) { + int fd = pipes[i]; + if (fd == -1) { + return; + } + if (fd != skipFd) { + close(pipes[i]); + } + } +} + +/** Executes a command in a child process. */ +static pid_t executeProcess(JNIEnv* env, char** commands, char** environment, + const char* workingDirectory, jobject inDescriptor, + jobject outDescriptor, jobject errDescriptor) { + int i, result, error; + + // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe. + int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; + for (i = 0; i < PIPE_COUNT; i++) { + if (pipe(pipes + i * 2) == -1) { + jniThrowIOException(env, errno); + closePipes(pipes, -1); + return -1; + } + } + int stdinIn = pipes[0]; + int stdinOut = pipes[1]; + int stdoutIn = pipes[2]; + int stdoutOut = pipes[3]; + int stderrIn = pipes[4]; + int stderrOut = pipes[5]; + int statusIn = pipes[6]; + int statusOut = pipes[7]; + + pid_t childPid = fork(); + + // If fork() failed... + if (childPid == -1) { + jniThrowIOException(env, errno); + closePipes(pipes, -1); + return -1; + } + + // If this is the child process... + if (childPid == 0) { + // Replace stdin, out, and err with pipes. + dup2(stdinIn, 0); + dup2(stdoutOut, 1); + dup2(stderrOut, 2); + + // Close all but statusOut. This saves some work in the next step. + closePipes(pipes, statusOut); + + // Make statusOut automatically close if execvp() succeeds. + fcntl(statusOut, F_SETFD, FD_CLOEXEC); + + // Close remaining open fds with the exception of statusOut. + closeNonStandardFds(statusOut); + + // Switch to working directory. + if (workingDirectory != NULL) { + if (chdir(workingDirectory) == -1) { + goto execFailed; + } + } + + // Set up environment. + if (environment != NULL) { + environ = environment; + } + + // Execute process. By convention, the first argument in the arg array + // should be the command itself. In fact, I get segfaults when this + // isn't the case. + execvp(commands[0], commands); + + // If we got here, execvp() failed or the working dir was invalid. + execFailed: + error = errno; + write(statusOut, &error, sizeof(int)); + close(statusOut); + exit(error); + } + + // This is the parent process. + + // Close child's pipe ends. + close(stdinIn); + close(stdoutOut); + close(stderrOut); + close(statusOut); + + // Check status pipe for an error code. If execvp() succeeds, the other + // end of the pipe should automatically close, in which case, we'll read + // nothing. + int count = read(statusIn, &result, sizeof(int)); + close(statusIn); + if (count > 0) { + jniThrowIOException(env, result); + + close(stdoutIn); + close(stdinOut); + close(stderrIn); + + return -1; + } + + // Fill in file descriptor wrappers. + jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn); + jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut); + jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn); + + return childPid; +} + +/** Converts a Java String[] to a 0-terminated char**. */ +static char** convertStrings(JNIEnv* env, jobjectArray javaArray) { + if (javaArray == NULL) { + return NULL; + } + + char** array = NULL; + jsize length = (*env)->GetArrayLength(env, javaArray); + array = (char**) malloc(sizeof(char*) * (length + 1)); + array[length] = 0; + jsize index; + for (index = 0; index < length; index++) { + jstring javaEntry = (jstring) (*env)->GetObjectArrayElement( + env, javaArray, index); + char* entry = (char*) (*env)->GetStringUTFChars( + env, javaEntry, NULL); + array[index] = entry; + } + + return array; +} + +/** Frees a char** which was converted from a Java String[]. */ +static void freeStrings(JNIEnv* env, jobjectArray javaArray, char** array) { + if (javaArray == NULL) { + return; + } + + jsize length = (*env)->GetArrayLength(env, javaArray); + jsize index; + for (index = 0; index < length; index++) { + jstring javaEntry = (jstring) (*env)->GetObjectArrayElement( + env, javaArray, index); + (*env)->ReleaseStringUTFChars(env, javaEntry, array[index]); + } + + free(array); +} + +/** + * Converts Java String[] to char** and delegates to executeProcess(). + */ +static pid_t java_lang_ProcessManager_exec( + JNIEnv* env, jclass clazz, jobjectArray javaCommands, + jobjectArray javaEnvironment, jstring javaWorkingDirectory, + jobject inDescriptor, jobject outDescriptor, jobject errDescriptor) { + + // Copy commands into char*[]. + char** commands = convertStrings(env, javaCommands); + + // Extract working directory string. + const char* workingDirectory = NULL; + if (javaWorkingDirectory != NULL) { + workingDirectory = (const char*) (*env)->GetStringUTFChars( + env, javaWorkingDirectory, NULL); + } + + // Convert environment array. + char** environment = convertStrings(env, javaEnvironment); + + pid_t result = executeProcess( + env, commands, environment, workingDirectory, + inDescriptor, outDescriptor, errDescriptor); + + // Temporarily clear exception so we can clean up. + jthrowable exception = (*env)->ExceptionOccurred(env); + (*env)->ExceptionClear(env); + + freeStrings(env, javaEnvironment, environment); + + // Clean up working directory string. + if (javaWorkingDirectory != NULL) { + (*env)->ReleaseStringUTFChars( + env, javaWorkingDirectory, workingDirectory); + } + + freeStrings(env, javaCommands, commands); + + // Re-throw exception if present. + if (exception != NULL) { + if ((*env)->Throw(env, exception) < 0) { + LOGE("Error rethrowing exception!"); + } + } + + return result; +} + +/** + * Looks up Java members. + */ +static void java_lang_ProcessManager_staticInitialize(JNIEnv* env, + jclass clazz) { +#ifdef ANDROID + char* fdString = getenv("ANDROID_PROPERTY_WORKSPACE"); + if (fdString) { + androidSystemPropertiesFd = atoi(fdString); + } +#endif + + onExitMethod = (*env)->GetMethodID(env, clazz, "onExit", "(II)V"); + if (onExitMethod == NULL) { + return; + } + + jclass fileDescriptorClass + = (*env)->FindClass(env, "java/io/FileDescriptor"); + if (fileDescriptorClass == NULL) { + return; + } + descriptorField = (*env)->GetFieldID(env, fileDescriptorClass, + "descriptor", "I"); + if (descriptorField == NULL) { + return; + } +} + +static JNINativeMethod methods[] = { + { "kill", "(I)V", (void*) java_lang_ProcessManager_kill }, + { "watchChildren", "()V", (void*) java_lang_ProcessManager_watchChildren }, + { "exec", "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;" + "Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;" + "Ljava/io/FileDescriptor;)I", (void*) java_lang_ProcessManager_exec }, + { "staticInitialize", "()V", + (void*) java_lang_ProcessManager_staticInitialize }, + { "close", "(Ljava/io/FileDescriptor;)V", + (void*) java_lang_ProcessManager_close }, +}; + +int register_java_lang_ProcessManager(JNIEnv* env) { + return jniRegisterNativeMethods( + env, "java/lang/ProcessManager", methods, NELEM(methods)); +} diff --git a/luni-kernel/src/main/native/java_lang_System.c b/luni-kernel/src/main/native/java_lang_System.c new file mode 100644 index 0000000..1c06a40 --- /dev/null +++ b/luni-kernel/src/main/native/java_lang_System.c @@ -0,0 +1,106 @@ +/* + * 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. + */ + +#include "JNIHelp.h" + +#include <stdlib.h> +#include <string.h> + + +/* + * public static native String getEnvByName(String name) + * + * (Calling it plain "getenv" might confuse GDB if you try to put a breakpoint + * on the libc version.) + */ +static jstring java_getEnvByName(JNIEnv* env, jclass clazz, jstring nameStr) +{ + jstring valueStr = NULL; + + if (nameStr != NULL) { + const char* name; + const char* val; + + name = (*env)->GetStringUTFChars(env, nameStr, NULL); + val = getenv(name); + if (val != NULL) + valueStr = (*env)->NewStringUTF(env, val); + + (*env)->ReleaseStringUTFChars(env, nameStr, name); + } else { + jniThrowException(env, "java/lang/NullPointerException", NULL); + } + + return valueStr; +} + +/* + * Pointer to complete environment, from Posix. + */ +extern char** environ; + +/* + * public static native String getEnvByIndex() + * + * (Calling it plain "getenv" might confuse GDB if you try to put a breakpoint + * on the libc version.) + */ +static jstring java_getEnvByIndex(JNIEnv* env, jclass clazz, jint index) +{ + jstring valueStr = NULL; + + char* entry = environ[index]; + if (entry != NULL) { + valueStr = (*env)->NewStringUTF(env, entry); + } + + return valueStr; +} + +/* + * public static native String setFieldImpl() + * + * Sets a field via JNI. Used for the standard streams, which are r/o + * otherwise. + */ +static void java_setFieldImpl(JNIEnv* env, jclass clazz, jstring name, jstring sig, jobject object) +{ + const char* fieldName = (*env)->GetStringUTFChars(env, name, NULL); + const char* fieldSig = (*env)->GetStringUTFChars(env, sig, NULL); + + jfieldID fieldID = (*env)->GetStaticFieldID(env, clazz, fieldName, fieldSig); + (*env)->SetStaticObjectField(env, clazz, fieldID, object); + + (*env)->ReleaseStringUTFChars(env, name, fieldName); + (*env)->ReleaseStringUTFChars(env, sig, fieldSig); +} + +/* + * JNI registration + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "getEnvByName", "(Ljava/lang/String;)Ljava/lang/String;", java_getEnvByName }, + { "getEnvByIndex", "(I)Ljava/lang/String;", java_getEnvByIndex }, + { "setFieldImpl", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V", java_setFieldImpl }, +}; + +int register_java_lang_System(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "java/lang/System", + gMethods, NELEM(gMethods)); +} + diff --git a/luni-kernel/src/main/native/sub.mk b/luni-kernel/src/main/native/sub.mk new file mode 100644 index 0000000..cd4ce92 --- /dev/null +++ b/luni-kernel/src/main/native/sub.mk @@ -0,0 +1,17 @@ +# This file is included by the top-level libcore Android.mk. +# It's not a normal makefile, so we don't include CLEAR_VARS +# or BUILD_*_LIBRARY. + +LOCAL_SRC_FILES := \ + java_lang_ProcessManager.c \ + java_lang_System.c + +LOCAL_C_INCLUDES += + +# Any shared/static libs that are listed here must also +# be listed in libs/nativehelper/Android.mk. +# TODO: fix this requirement + +LOCAL_SHARED_LIBRARIES += + +LOCAL_STATIC_LIBRARIES += diff --git a/luni-kernel/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java b/luni-kernel/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java new file mode 100644 index 0000000..3791191 --- /dev/null +++ b/luni-kernel/src/test/java/tests/api/org/apache/harmony/kernel/dalvik/ThreadsTest.java @@ -0,0 +1,298 @@ +/* + * 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 tests.api.org.apache.harmony.kernel.dalvik; + +import java.lang.reflect.Field; + +import junit.framework.Assert; +import junit.framework.TestCase; +import sun.misc.Unsafe; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetNew; + +/** + * Tests for the <code>park()</code> functionality of {@link Unsafe}. + */ +@TestTargetClass(Unsafe.class) +public class ThreadsTest extends TestCase { + private static Unsafe UNSAFE = null; + private static RuntimeException INITIALIZEFAILED = null; + + static { + /* + * Set up {@link #UNSAFE}. This subverts the access check to + * get the unique Unsafe instance. We can do this because + * there's no security manager installed when running the + * test. + */ + try { + Field field = Unsafe.class.getDeclaredField("THE_ONE"); + field.setAccessible(true); + + UNSAFE = (Unsafe) field.get(null); + } catch (NoSuchFieldException ex) { + INITIALIZEFAILED = new RuntimeException(ex); + } catch (IllegalAccessException ex) { + INITIALIZEFAILED = new RuntimeException(ex); + } + } + + /** Test the case where the park times out. */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "unpark", + args = {Object.class} + ) + public void test_parkFor_1() { + Parker parker = new Parker(false, 500); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(1000, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(500); + } + + /** Test the case where the unpark happens before the timeout. */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "unpark", + args = {Object.class} + ) + public void test_parkFor_2() { + Parker parker = new Parker(false, 1000); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(300, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(300); + } + + /** Test the case where the thread is preemptively unparked. */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "unpark", + args = {Object.class} + ) + public void test_parkFor_3() { + Parker parker = new Parker(false, 1000); + Thread parkerThread = new Thread(parker); + + UNSAFE.unpark(parkerThread); + parkerThread.start(); + parker.assertDurationIsInRange(0); + } + + /** Test the case where the park times out. */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "unpark", + args = {Object.class} + ) + public void test_parkUntil_1() { + Parker parker = new Parker(true, 500); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(1000, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(500); + } + + /** Test the case where the unpark happens before the timeout. */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "unpark", + args = {Object.class} + ) + public void test_parkUntil_2() { + Parker parker = new Parker(true, 1000); + Thread parkerThread = new Thread(parker); + Thread waiterThread = + new Thread(new WaitAndUnpark(300, parkerThread)); + + parkerThread.start(); + waiterThread.start(); + parker.assertDurationIsInRange(300); + } + + /** Test the case where the thread is preemptively unparked. */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "unpark", + args = {Object.class} + ) + public void test_parkUntil_3() { + Parker parker = new Parker(true, 1000); + Thread parkerThread = new Thread(parker); + + UNSAFE.unpark(parkerThread); + parkerThread.start(); + parker.assertDurationIsInRange(0); + } + + // TODO: Add more tests. + + /** + * Helper <code>Runnable</code> for tests, which parks for or until + * the indicated value, noting the duration of time actually parked. + */ + private static class Parker implements Runnable { + /** whether {@link #amount} is milliseconds to wait in an + * absolute fashion (<code>true</code>) or nanoseconds to wait + * in a relative fashion (<code>false</code>) */ + private final boolean absolute; + + /** amount to wait (see above) */ + private final long amount; + + /** whether the run has completed */ + private boolean completed; + + /** recorded start time */ + private long startMillis; + + /** recorded end time */ + private long endMillis; + + /** + * Construct an instance. + * + * @param absolute whether to use an absolute time or not; in + * either case, this constructor takes a duration to park for + * @param parkMillis the number of milliseconds to be parked + */ + public Parker(boolean absolute, long parkMillis) { + this.absolute = absolute; + + // Multiply by 1000000 because parkFor() takes nanoseconds. + this.amount = absolute ? parkMillis : parkMillis * 1000000; + } + + public void run() { + boolean absolute = this.absolute; + long amount = this.amount; + long start = System.currentTimeMillis(); + + if (absolute) { + UNSAFE.park(true, start + amount); + } else { + UNSAFE.park(false, amount); + } + + long end = System.currentTimeMillis(); + + synchronized (this) { + startMillis = start; + endMillis = end; + completed = true; + notifyAll(); + } + } + + /** + * Wait for the test to complete and return the duration. + * + * @param maxWaitMillis the maximum amount of time to + * wait for the test to complete + * @return the duration in milliseconds + */ + public long getDurationMillis(long maxWaitMillis) { + synchronized (this) { + if (! completed) { + try { + wait(maxWaitMillis); + } catch (InterruptedException ex) { + // Ignore it. + } + if (! completed) { + Assert.fail("parker hanging"); + } + } + + return endMillis - startMillis; + } + } + + /** + * Asserts that the actual duration is within 5% of the + * given expected time. + * + * @param expectedMillis the expected duration, in milliseconds + */ + public void assertDurationIsInRange(long expectedMillis) { + /* + * Allow a bit more slop for the maximum on "expected + * instantaneous" results. + */ + long minimum = (long) ((double) expectedMillis * 0.95); + long maximum = + Math.max((long) ((double) expectedMillis * 1.05), 10); + long waitMillis = Math.max(expectedMillis * 10, 10); + long duration = getDurationMillis(waitMillis); + + if (duration < minimum) { + Assert.fail("expected duration: " + expectedMillis + + "; actual too short: " + duration); + } else if (duration > maximum) { + Assert.fail("expected duration: " + expectedMillis + + "; actual too long: " + duration); + } + } + } + + /** + * Helper <code>Runnable</code> for tests, which waits for the + * specified amount of time and then unparks an indicated thread. + */ + private static class WaitAndUnpark implements Runnable { + private final long waitMillis; + private final Thread thread; + + public WaitAndUnpark(long waitMillis, Thread thread) { + this.waitMillis = waitMillis; + this.thread = thread; + } + + public void run() { + try { + Thread.sleep(waitMillis); + } catch (InterruptedException ex) { + throw new RuntimeException("shouldn't happen", ex); + } + + UNSAFE.unpark(thread); + } + } + + @Override + protected void setUp() throws Exception { + if (INITIALIZEFAILED != null) + throw INITIALIZEFAILED; + } +} |