diff options
83 files changed, 11987 insertions, 312 deletions
@@ -50,4 +50,26 @@ $(info * libcore tests are skipped because environment variable LIBCORE_SKIP_TES $(info ********************************************************************************) endif + +# +# "m dalvik-host" for quick minimal host build +# + +.PHONY: dalvik-host +dalvik-host: \ + dalvik \ + $(HOST_OUT)/bin/dalvikvm \ + $(HOST_OUT)/bin/dexopt \ + $(HOST_OUT)/lib/libjavacore.so \ + $(HOST_OUT)/lib/libjavacrypto.so \ + $(HOST_OUT)/lib/libjavacoretests.so \ + cacerts-host \ + core-hostdex \ + conscrypt-hostdex \ + okhttp-hostdex \ + bouncycastle-hostdex \ + apache-xml-hostdex \ + apache-harmony-tests-hostdex \ + $(call intermediates-dir-for,JAVA_LIBRARIES,core-tests,,COMMON)/classes.jar + include $(subdir_makefiles) @@ -17,8 +17,8 @@ libcore_to_document := \ dalvik/src/main/java/dalvik/annotation \ dalvik/src/main/java/dalvik/bytecode \ json/src/main/java \ - libart/src/main/java/dalvik \ - libart/src/main/java/java \ + libdvm/src/main/java/dalvik \ + libdvm/src/main/java/java \ luni/src/main/java/android \ luni/src/main/java/java \ luni/src/main/java/javax \ diff --git a/JavaLibrary.mk b/JavaLibrary.mk index c857390..8935b9a 100644 --- a/JavaLibrary.mk +++ b/JavaLibrary.mk @@ -62,6 +62,7 @@ ifneq ($(EMMA_INSTRUMENT_STATIC),true) endif endif +libdvm_core_src_files += $(common_core_src_files) $(call all-main-java-files-under,libdvm) libart_core_src_files += $(common_core_src_files) $(call all-main-java-files-under,libart) local_javac_flags=-encoding UTF-8 @@ -75,6 +76,18 @@ local_javac_flags+=-Xmaxwarns 9999999 # Definitions to make the core library. include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(libdvm_core_src_files) +LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) +LOCAL_NO_STANDARD_LIBRARIES := true +LOCAL_JAVACFLAGS := $(local_javac_flags) +LOCAL_DX_FLAGS := --core-library +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := core +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk +LOCAL_REQUIRED_MODULES := tzdata +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) LOCAL_SRC_FILES := $(libart_core_src_files) LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true @@ -95,7 +108,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(test_src_files) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-libart okhttp core-junit bouncycastle +LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit okhttp LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support sqlite-jdbc mockwebserver nist-pkix-tests LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE := core-tests @@ -109,7 +122,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,support) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-libart core-junit bouncycastle +LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE := core-tests-support LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk @@ -122,7 +135,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under, jsr166-tests) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-libart core-junit +LOCAL_JAVA_LIBRARIES := core core-junit LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE := jsr166-tests LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk @@ -156,6 +169,18 @@ include $(BUILD_HOST_JAVA_LIBRARY) # Definitions to make the core library. include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(libdvm_core_src_files) +LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) +LOCAL_NO_STANDARD_LIBRARIES := true +LOCAL_JAVACFLAGS := $(local_javac_flags) +LOCAL_DX_FLAGS := --core-library +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := core-hostdex +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk +LOCAL_REQUIRED_MODULES := tzdata-host +include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) + +include $(CLEAR_VARS) LOCAL_SRC_FILES := $(libart_core_src_files) LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true @@ -175,8 +200,7 @@ ifeq ($(LIBCORE_SKIP_TESTS),) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(test_src_files) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) - LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_JAVA_LIBRARIES := core-libart-hostdex okhttp-hostdex bouncycastle-hostdex core-junit-hostdex core-tests-support-hostdex + LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-junit-hostdex core-tests-support-hostdex okhttp-hostdex LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc-host mockwebserver-host nist-pkix-tests-host LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE_TAGS := optional @@ -190,8 +214,7 @@ ifeq ($(LIBCORE_SKIP_TESTS),) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,support) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) - LOCAL_NO_STANDARD_LIBRARIES := true - LOCAL_JAVA_LIBRARIES := core-libart-hostdex core-junit-hostdex bouncycastle-hostdex + LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-junit-hostdex LOCAL_JAVACFLAGS := $(local_javac_flags) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := core-tests-support-hostdex diff --git a/NativeCode.mk b/NativeCode.mk index 12e8114..0ae615e 100644 --- a/NativeCode.mk +++ b/NativeCode.mk @@ -66,7 +66,7 @@ $(foreach dir, \ core_c_includes := libcore/include $(LOCAL_C_INCLUDES) core_shared_libraries := $(LOCAL_SHARED_LIBRARIES) core_static_libraries := $(LOCAL_STATIC_LIBRARIES) -core_cflags := -Wall -Wextra -Werror +core_cflags := $(LOCAL_CFLAGS) -Wall -Wextra -Werror core_cppflags += -std=gnu++11 core_test_files := \ diff --git a/benchmarks/Android.mk b/benchmarks/Android.mk index c0a38a0..b91894f 100644 --- a/benchmarks/Android.mk +++ b/benchmarks/Android.mk @@ -1,39 +1,34 @@ -# -*- mode: makefile -*- -# Copyright (C) 2013 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. - LOCAL_PATH:= $(call my-dir) +################################################## +include $(CLEAR_VARS) ifeq ($(LIBCORE_SKIP_TESTS),) +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) -################################################## -include $(CLEAR_VARS) LOCAL_MODULE := benchmarks -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := caliper-prebuilt core-tests -LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-libart conscrypt core-junit bouncycastle framework + +LOCAL_STATIC_JAVA_LIBRARIES := \ + caliper-prebuilt \ + core-tests + +LOCAL_JAVA_LIBRARIES := \ + bouncycastle \ + conscrypt \ + core + LOCAL_MODULE_TAGS := tests + LOCAL_MODULE_PATH := $(PRODUCT_OUT)/data/caliperperf -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + include $(BUILD_JAVA_LIBRARY) ################################################## # Prebuilt Java libraries include $(CLEAR_VARS) -LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := caliper-prebuilt:libs/caliper.jar -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk -include $(BUILD_MULTI_PREBUILT) +LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \ + caliper-prebuilt:libs/caliper.jar + +include $(BUILD_MULTI_PREBUILT) endif diff --git a/dalvik/src/main/java/dalvik/system/DexClassLoader.java b/dalvik/src/main/java/dalvik/system/DexClassLoader.java index ac2a70a..a645f42 100644 --- a/dalvik/src/main/java/dalvik/system/DexClassLoader.java +++ b/dalvik/src/main/java/dalvik/system/DexClassLoader.java @@ -24,9 +24,9 @@ import java.io.File; * installed as part of an application. * * <p>This class loader requires an application-private, writable directory to - * cache optimized classes. Use {@code Context.getDir(String, int)} to create + * cache optimized classes. Use {@code Context.getCodeCacheDir()} to create * such a directory: <pre> {@code - * File dexOutputDir = context.getDir("dex", 0); + * File dexOutputDir = context.getCodeCacheDir(); * }</pre> * * <p><strong>Do not cache optimized classes on external storage.</strong> diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt index 698ebc5..94b1a59 100644 --- a/expectations/knownfailures.txt +++ b/expectations/knownfailures.txt @@ -1377,5 +1377,34 @@ description: "Known failure in GregorianCalendarTest", bug: 12778197, name: "org.apache.harmony.tests.java.util.GregorianCalendarTest#test_computeTime" +}, +{ + description: "SpdyConnection issue https://github.com/square/okhttp/issues/644 crashes the test app. Android does not provide SPDY/HTTP_2 connections by default so have been suppressed.", + bug: 14462336, + names: [ + "com.squareup.okhttp.internal.spdy.SpdyConnectionTest", + "com.squareup.okhttp.internal.http.HttpOverHttp20Draft09Test", + "com.squareup.okhttp.internal.http.HttpOverSpdy3Test", + "com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_SPDY_3", + "com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_HTTP_2", + "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPost_SPDY_3", + "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPost_HTTP_2", + "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPut_SPDY_3", + "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPut_HTTP_2" + ] +}, +{ + description: "Okhttp test hardcodes the TLS version expected.", + bug: 14462336, + names: [ + "com.squareup.okhttp.internal.http.URLConnectionTest#sslFallbackNotUsedWhenRecycledConnectionFails" + ] +}, +{ + description: "The test relies on SimpleDateFormat zzz producing GMT not GMT+00:00 as it does on Android. Android issue 66136.", + bug: 14462336, + names: [ + "com.squareup.okhttp.internal.http.HttpResponseCacheTest#setIfModifiedSince" + ] } ] diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java index 49e7868..d5d8191 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java @@ -23,8 +23,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.security.CodeSigner; import java.security.Permission; import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Enumeration; import java.util.Vector; import java.util.jar.Attributes; @@ -92,6 +95,12 @@ public class JarFileTest extends TestCase { private final String emptyEntry3 = "svgunit.js"; + private static final String VALID_CHAIN_JAR = "hyts_signed_validChain.jar"; + + private static final String INVALID_CHAIN_JAR = "hyts_signed_invalidChain.jar"; + + private static final String AMBIGUOUS_SIGNERS_JAR = "hyts_signed_ambiguousSignerArray.jar"; + private File resources; // custom security manager @@ -641,6 +650,73 @@ public class JarFileTest extends TestCase { + jarName + "\"", foundCerts); } + private static class Results { + public Certificate[] certificates; + public CodeSigner[] signers; + } + + private Results getSignedJarCerts(String jarName) throws Exception { + Support_Resources.copyFile(resources, null, jarName); + + File file = new File(resources, jarName); + Results results = new Results(); + + JarFile jarFile = new JarFile(file, true, ZipFile.OPEN_READ); + try { + + Enumeration<JarEntry> e = jarFile.entries(); + while (e.hasMoreElements()) { + JarEntry entry = e.nextElement(); + InputStream is = jarFile.getInputStream(entry); + // Skip bytes because we have to read the entire file for it to read signatures. + is.skip(entry.getSize()); + is.close(); + Certificate[] certs = entry.getCertificates(); + CodeSigner[] signers = entry.getCodeSigners(); + if (certs != null && certs.length > 0) { + results.certificates = certs; + results.signers = signers; + break; + } + } + } finally { + jarFile.close(); + } + + return results; + } + + public void testJarFile_Signed_ValidChain() throws Exception { + Results result = getSignedJarCerts(VALID_CHAIN_JAR); + assertNotNull(result); + assertEquals(Arrays.deepToString(result.certificates), 3, result.certificates.length); + assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length); + assertEquals(3, result.signers[0].getSignerCertPath().getCertificates().size()); + assertEquals("CN=fake-chain", ((X509Certificate) result.certificates[0]).getSubjectDN().toString()); + assertEquals("CN=intermediate1", ((X509Certificate) result.certificates[1]).getSubjectDN().toString()); + assertEquals("CN=root1", ((X509Certificate) result.certificates[2]).getSubjectDN().toString()); + } + + public void testJarFile_Signed_InvalidChain() throws Exception { + Results result = getSignedJarCerts(INVALID_CHAIN_JAR); + assertNotNull(result); + assertEquals(Arrays.deepToString(result.certificates), 3, result.certificates.length); + assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length); + assertEquals(3, result.signers[0].getSignerCertPath().getCertificates().size()); + assertEquals("CN=fake-chain", ((X509Certificate) result.certificates[0]).getSubjectDN().toString()); + assertEquals("CN=intermediate1", ((X509Certificate) result.certificates[1]).getSubjectDN().toString()); + assertEquals("CN=root1", ((X509Certificate) result.certificates[2]).getSubjectDN().toString()); + } + + public void testJarFile_Signed_AmbiguousSigners() throws Exception { + Results result = getSignedJarCerts(AMBIGUOUS_SIGNERS_JAR); + assertNotNull(result); + assertEquals(Arrays.deepToString(result.certificates), 2, result.certificates.length); + assertEquals(Arrays.deepToString(result.signers), 2, result.signers.length); + assertEquals(1, result.signers[0].getSignerCertPath().getCertificates().size()); + assertEquals(1, result.signers[1].getSignerCertPath().getCertificates().size()); + } + /* * The jar created by 1.4 which does not provide a * algorithm-Digest-Manifest-Main-Attributes entry in .SF file. diff --git a/libdvm/src/main/java/dalvik/system/VMRuntime.java b/libdvm/src/main/java/dalvik/system/VMRuntime.java new file mode 100644 index 0000000..f117b70 --- /dev/null +++ b/libdvm/src/main/java/dalvik/system/VMRuntime.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dalvik.system; + +import java.util.HashMap; +import java.util.Map; + +/** + * Provides an interface to VM-global, Dalvik-specific features. + * An application cannot create its own Runtime instance, and must obtain + * one from the getRuntime method. + * + * @hide + */ +public final class VMRuntime { + + /** + * Holds the VMRuntime singleton. + */ + private static final VMRuntime THE_ONE = new VMRuntime(); + + private static final Map<String, String> ABI_TO_INSTRUCTION_SET_MAP + = new HashMap<String, String>(); + static { + ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm"); + ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm"); + ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips"); + ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86"); + } + + private int targetSdkVersion; + + /** + * Prevents this class from being instantiated. + */ + private VMRuntime() { + } + + /** + * Returns the object that represents the VM instance's Dalvik-specific + * runtime environment. + * + * @return the runtime object + */ + public static VMRuntime getRuntime() { + return THE_ONE; + } + + /** + * Returns a copy of the VM's command-line property settings. + * These are in the form "name=value" rather than "-Dname=value". + */ + public native String[] properties(); + + /** + * Returns the VM's boot class path. + */ + public native String bootClassPath(); + + /** + * Returns the VM's class path. + */ + public native String classPath(); + + /** + * Returns the VM's version. + */ + public native String vmVersion(); + + /** + * Returns the name of the shared library providing the VM implementation. + */ + public native String vmLibrary(); + + /** + * Returns the VM's instruction set. + */ + public String vmInstructionSet() { + return ""; + } + + /** + * Returns whether the VM is running in 64-bit mode. + */ + public boolean is64Bit() { + return false; + } + + /** + * Returns whether the VM is running with JNI checking enabled. + */ + public native boolean isCheckJniEnabled(); + + /** + * Gets the current ideal heap utilization, represented as a number + * between zero and one. After a GC happens, the Dalvik heap may + * be resized so that (size of live objects) / (size of heap) is + * equal to this number. + * + * @return the current ideal heap utilization + */ + public native float getTargetHeapUtilization(); + + /** + * Sets the current ideal heap utilization, represented as a number + * between zero and one. After a GC happens, the Dalvik heap may + * be resized so that (size of live objects) / (size of heap) is + * equal to this number. + * + * <p>This is only a hint to the garbage collector and may be ignored. + * + * @param newTarget the new suggested ideal heap utilization. + * This value may be adjusted internally. + * @return the previous ideal heap utilization + * @throws IllegalArgumentException if newTarget is <= 0.0 or >= 1.0 + */ + public float setTargetHeapUtilization(float newTarget) { + if (newTarget <= 0.0f || newTarget >= 1.0f) { + throw new IllegalArgumentException(newTarget + + " out of range (0,1)"); + } + /* Synchronize to make sure that only one thread gets + * a given "old" value if both update at the same time. + * Allows for reliable save-and-restore semantics. + */ + synchronized (this) { + float oldTarget = getTargetHeapUtilization(); + nativeSetTargetHeapUtilization(newTarget); + return oldTarget; + } + } + + /** + * Sets the target SDK version. Should only be called before the + * app starts to run, because it may change the VM's behavior in + * dangerous ways. Use 0 to mean "current" (since callers won't + * necessarily know the actual current SDK version, and the + * allocated version numbers start at 1), and 10000 to mean + * CUR_DEVELOPMENT. + */ + public synchronized void setTargetSdkVersion(int targetSdkVersion) { + this.targetSdkVersion = targetSdkVersion; + setTargetSdkVersionNative(this.targetSdkVersion); + } + + /** + * Gets the target SDK version. See {@link #setTargetSdkVersion} for + * special values. + */ + public synchronized int getTargetSdkVersion() { + return targetSdkVersion; + } + + private native void setTargetSdkVersionNative(int targetSdkVersion); + + /** + * This method exists for binary compatibility. It was part of a + * heap sizing API which was removed in Android 3.0 (Honeycomb). + */ + @Deprecated + public long getMinimumHeapSize() { + return 0; + } + + /** + * This method exists for binary compatibility. It was part of a + * heap sizing API which was removed in Android 3.0 (Honeycomb). + */ + @Deprecated + public long setMinimumHeapSize(long size) { + return 0; + } + + /** + * This method exists for binary compatibility. It used to + * perform a garbage collection that cleared SoftReferences. + */ + @Deprecated + public void gcSoftReferences() {} + + /** + * This method exists for binary compatibility. It is equivalent + * to {@link System#runFinalization}. + */ + @Deprecated + public void runFinalizationSync() { + System.runFinalization(); + } + + /** + * Implements setTargetHeapUtilization(). + * + * @param newTarget the new suggested ideal heap utilization. + * This value may be adjusted internally. + */ + private native void nativeSetTargetHeapUtilization(float newTarget); + + /** + * This method exists for binary compatibility. It was part of + * the external allocation API which was removed in Android 3.0 (Honeycomb). + */ + @Deprecated + public boolean trackExternalAllocation(long size) { + return true; + } + + /** + * This method exists for binary compatibility. It was part of + * the external allocation API which was removed in Android 3.0 (Honeycomb). + */ + @Deprecated + public void trackExternalFree(long size) {} + + /** + * This method exists for binary compatibility. It was part of + * the external allocation API which was removed in Android 3.0 (Honeycomb). + */ + @Deprecated + public long getExternalBytesAllocated() { + return 0; + } + + /** + * Tells the VM to enable the JIT compiler. If the VM does not have a JIT + * implementation, calling this method should have no effect. + */ + public native void startJitCompilation(); + + /** + * Tells the VM to disable the JIT compiler. If the VM does not have a JIT + * implementation, calling this method should have no effect. + */ + public native void disableJitCompilation(); + + /** + * Returns an array allocated in an area of the Java heap where it will never be moved. + * This is used to implement native allocations on the Java heap, such as DirectByteBuffers + * and Bitmaps. + */ + public native Object newNonMovableArray(Class<?> componentType, int length); + + /** + * Returns an array of at least minLength, but potentially larger. The increased size comes from + * avoiding any padding after the array. The amount of padding varies depending on the + * componentType and the memory allocator implementation. + */ + public Object newUnpaddedArray(Class<?> componentType, int minLength) { + // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc, + // allocations are 8byte aligned so having 4bytes of array data avoids padding. + if (!componentType.isPrimitive()) { + int size = ((minLength & 1) == 0) ? minLength + 1 : minLength; + return java.lang.reflect.Array.newInstance(componentType, size); + } else if (componentType == char.class) { + int bytes = 20 + (2 * minLength); + int alignedUpBytes = (bytes + 7) & -8; + int dataBytes = alignedUpBytes - 20; + int size = dataBytes / 2; + return new char[size]; + } else if (componentType == int.class) { + int size = ((minLength & 1) == 0) ? minLength + 1 : minLength; + return new int[size]; + } else if (componentType == byte.class) { + int bytes = 20 + minLength; + int alignedUpBytes = (bytes + 7) & -8; + int dataBytes = alignedUpBytes - 20; + int size = dataBytes; + return new byte[size]; + } else if (componentType == boolean.class) { + int bytes = 20 + minLength; + int alignedUpBytes = (bytes + 7) & -8; + int dataBytes = alignedUpBytes - 20; + int size = dataBytes; + return new boolean[size]; + } else if (componentType == short.class) { + int bytes = 20 + (2 * minLength); + int alignedUpBytes = (bytes + 7) & -8; + int dataBytes = alignedUpBytes - 20; + int size = dataBytes / 2; + return new short[size]; + } else if (componentType == float.class) { + int size = ((minLength & 1) == 0) ? minLength + 1 : minLength; + return new float[size]; + } else if (componentType == long.class) { + return new long[minLength]; + } else if (componentType == double.class) { + return new double[minLength]; + } else { + assert componentType == void.class; + throw new IllegalArgumentException("Can't allocate an array of void"); + } + } + + /** + * Returns the address of array[0]. This differs from using JNI in that JNI might lie and + * give you the address of a copy of the array when in forcecopy mode. + */ + public native long addressOf(Object array); + + /** + * Removes any growth limits, allowing the application to allocate + * up to the maximum heap size. + */ + public native void clearGrowthLimit(); + + /** + * Returns true if either a Java debugger or native debugger is active. + */ + public native boolean isDebuggerActive(); + + /** + * Registers a native allocation so that the heap knows about it and performs GC as required. + * If the number of native allocated bytes exceeds the native allocation watermark, the + * function requests a concurrent GC. If the native bytes allocated exceeds a second higher + * watermark, it is determined that the application is registering native allocations at an + * unusually high rate and a GC is performed inside of the function to prevent memory usage + * from excessively increasing. + */ + public native void registerNativeAllocation(int bytes); + + /** + * Registers a native free by reducing the number of native bytes accounted for. + */ + public native void registerNativeFree(int bytes); + + /** + * Let the heap know of the new process state. This can change allocation and garbage collection + * behavior regarding trimming and compaction. + */ + public native void updateProcessState(int state); + + /** + * Fill in dex caches with classes, fields, and methods that are + * already loaded. Typically used after Zygote preloading. + */ + public native void preloadDexCaches(); + + /** + * Register application info + */ + public static void registerAppInfo(String appDir, String processName, String pkgname) { + // Nothing to do in dalvik. + } + + /** + * Returns the runtime instruction set corresponding to a given ABI. Multiple + * compatible ABIs might map to the same instruction set. For example + * {@code armeabi-v7a} and {@code armeabi} might map to the instruction set {@code arm}. + * + * This influences the compilation of the applications classes. + */ + public static String getInstructionSet(String abi) { + final String instructionSet = ABI_TO_INSTRUCTION_SET_MAP.get(abi); + if (instructionSet == null) { + throw new IllegalArgumentException("Unsupported ABI: " + abi); + } + + return instructionSet; + } + + public static boolean is64BitInstructionSet(String instructionSet) { + return false; + } + + public static boolean is64BitAbi(String abi) { + return false; + } +} diff --git a/libdvm/src/main/java/dalvik/system/VMStack.java b/libdvm/src/main/java/dalvik/system/VMStack.java new file mode 100644 index 0000000..bae1829 --- /dev/null +++ b/libdvm/src/main/java/dalvik/system/VMStack.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dalvik.system; + +/** + * Provides a limited interface to the Dalvik VM stack. This class is mostly + * used for implementing security checks. + * + * @hide + */ +public final class VMStack { + /** + * Returns the defining class loader of the caller's caller. + * + * @return the requested class loader, or {@code null} if this is the + * bootstrap class loader. + */ + native public static ClassLoader getCallingClassLoader(); + + /** + * Returns the class of the caller's caller's caller. + * + * @return the requested class, or {@code null}. + */ + native public static Class<?> getStackClass2(); + + /** + * Creates an array of classes from the methods at the top of the stack. + * We continue until we reach the bottom of the stack or exceed the + * specified maximum depth. + * <p> + * The topmost stack frame (this method) and the one above that (the + * caller) are excluded from the array. Frames with java.lang.reflect + * classes are skipped over. + * <p> + * The classes in the array are the defining classes of the methods. + * <p> + * This is similar to Harmony's VMStack.getClasses, except that this + * implementation doesn't have a concept of "privileged" frames. + * + * @param maxDepth + * maximum number of classes to return, or -1 for all + * @return an array with classes for the most-recent methods on the stack + */ + native public static Class<?>[] getClasses(int maxDepth); + + /** + * Returns the first ClassLoader on the call stack that isn't either of + * the passed-in ClassLoaders. + */ + public static ClassLoader getClosestUserClassLoader(ClassLoader bootstrap, + ClassLoader system) { + Class<?>[] stackClasses = VMStack.getClasses(-1); + for (Class<?> stackClass : stackClasses) { + ClassLoader loader = stackClass.getClassLoader(); + if (loader != null && loader != bootstrap && loader != system) { + return loader; + } + } + return null; + } + + /** + * Retrieves the stack trace from the specified thread. + * + * @param t + * thread of interest + * @return an array of stack trace elements, or null if the thread + * doesn't have a stack trace (e.g. because it exited) + */ + native public static StackTraceElement[] getThreadStackTrace(Thread t); + + /** + * Retrieves a partial stack trace from the specified thread into + * the provided array. + * + * @param t + * thread of interest + * @param stackTraceElements + * preallocated array for use when only the top of stack is + * desired. Unused elements will be filled with null values. + * @return the number of elements filled + */ + native public static int fillStackTraceElements(Thread t, + StackTraceElement[] stackTraceElements); +} diff --git a/libdvm/src/main/java/java/lang/Class.java b/libdvm/src/main/java/java/lang/Class.java new file mode 100644 index 0000000..af6278d --- /dev/null +++ b/libdvm/src/main/java/java/lang/Class.java @@ -0,0 +1,1354 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2006-2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +import com.android.dex.Dex; +import dalvik.system.VMStack; +import java.io.InputStream; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.net.URL; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import org.apache.harmony.kernel.vm.StringUtils; +import libcore.reflect.AnnotationAccess; +import libcore.reflect.GenericSignatureParser; +import libcore.reflect.InternalNames; +import libcore.reflect.Types; +import libcore.util.BasicLruCache; +import libcore.util.CollectionUtils; +import libcore.util.EmptyArray; + +/** + * The in-memory representation of a Java class. This representation serves as + * the starting point for querying class-related information, a process usually + * called "reflection". There are basically three types of {@code Class} + * instances: those representing real classes and interfaces, those representing + * primitive types, and those representing array classes. + * + * <h4>Class instances representing object types (classes or interfaces)</h4> + * <p> + * These represent an ordinary class or interface as found in the class + * hierarchy. The name associated with these {@code Class} instances is simply + * the fully qualified class name of the class or interface that it represents. + * In addition to this human-readable name, each class is also associated by a + * so-called <em>signature</em>, which is the letter "L", followed by the + * class name and a semicolon (";"). The signature is what the runtime system + * uses internally for identifying the class (for example in a DEX file). + * </p> + * <h4>Classes representing primitive types</h4> + * <p> + * These represent the standard Java primitive types and hence share their + * names (for example "int" for the {@code int} primitive type). Although it is + * not possible to create new instances based on these {@code Class} instances, + * they are still useful for providing reflection information, and as the + * component type of array classes. There is one {@code Class} instance for each + * primitive type, and their signatures are: + * </p> + * <ul> + * <li>{@code B} representing the {@code byte} primitive type</li> + * <li>{@code S} representing the {@code short} primitive type</li> + * <li>{@code I} representing the {@code int} primitive type</li> + * <li>{@code J} representing the {@code long} primitive type</li> + * <li>{@code F} representing the {@code float} primitive type</li> + * <li>{@code D} representing the {@code double} primitive type</li> + * <li>{@code C} representing the {@code char} primitive type</li> + * <li>{@code Z} representing the {@code boolean} primitive type</li> + * <li>{@code V} representing void function return values</li> + * </ul> + * <p> + * <h4>Classes representing array classes</h4> + * <p> + * These represent the classes of Java arrays. There is one such {@code Class} + * instance per combination of array leaf component type and arity (number of + * dimensions). In this case, the name associated with the {@code Class} + * consists of one or more left square brackets (one per dimension in the array) + * followed by the signature of the class representing the leaf component type, + * which can be either an object type or a primitive type. The signature of a + * {@code Class} representing an array type is the same as its name. Examples + * of array class signatures are: + * </p> + * <ul> + * <li>{@code [I} representing the {@code int[]} type</li> + * <li>{@code [Ljava/lang/String;} representing the {@code String[]} type</li> + * <li>{@code [[[C} representing the {@code char[][][]} type (three dimensions!)</li> + * </ul> + */ +public final class Class<T> implements Serializable, AnnotatedElement, GenericDeclaration, Type { + + private static final long serialVersionUID = 3206093459760846163L; + + /** + * Class def index from dex file. An index of -1 indicates that there is no class definition, + * for example for an array type. + */ + private transient int dexClassDefIndex; + + /** The type index of this class within the dex file that defines it. */ + private transient int dexTypeIndex; + + /** + * Have we computed the type and class def indices? Volatile to avoid double check locking bugs. + */ + private transient volatile boolean dexIndicesInitialized; + + /** + * Lazily computed name of this class; always prefer calling getName(). + */ + private transient String name; + + private Class() { + // Prevent this class to be instantiated, instance + // should be created by JVM only + } + + /** + * Returns the dex file from which this class was loaded. + * @hide + */ + public native Dex getDex(); + + /** Lazily compute indices in to Dex */ + private synchronized void computeDexIndices() { + if (!dexIndicesInitialized) { + Dex dex = getDex(); + dexTypeIndex = dex.findTypeIndex(InternalNames.getInternalName(this)); + if (dexTypeIndex < 0) { + dexTypeIndex = -1; + dexClassDefIndex = -1; + } else { + dexClassDefIndex = dex.findClassDefIndexFromTypeIndex(dexTypeIndex); + } + dexIndicesInitialized = true; + } + } + + /** + * The class def of this class in its own Dex, or -1 if there is no class def. + * + * @hide + */ + public int getDexClassDefIndex() { + if (!dexIndicesInitialized) { + computeDexIndices(); + } + return dexClassDefIndex; + } + + /** + * The type index of this class in its own Dex, or -1 if it is unknown. If a class is referenced + * by multiple Dex files, it will have a different type index in each. Dex files support 65534 + * type indices, with 65535 representing no index. + * + * @hide + */ + public int getDexTypeIndex() { + if (!dexIndicesInitialized) { + computeDexIndices(); + } + return dexTypeIndex; + } + + /** + * Returns a {@code Class} object which represents the class with the + * given name. The name should be the name of a non-primitive class, as described in + * the {@link Class class definition}. + * Primitive types can not be found using this method; use {@code int.class} or {@code Integer.TYPE} instead. + * + * <p>If the class has not yet been loaded, it is loaded and initialized + * first. This is done through either the class loader of the calling class + * or one of its parent class loaders. It is possible that a static initializer is run as + * a result of this call. + * + * @throws ClassNotFoundException + * if the requested class can not be found. + * @throws LinkageError + * if an error occurs during linkage + * @throws ExceptionInInitializerError + * if an exception occurs during static initialization of a + * class. + */ + public static Class<?> forName(String className) throws ClassNotFoundException { + return forName(className, true, VMStack.getCallingClassLoader()); + } + + /** + * Returns a {@code Class} object which represents the class with the + * given name. The name should be the name of a non-primitive class, as described in + * the {@link Class class definition}. + * Primitive types can not be found using this method; use {@code int.class} or {@code Integer.TYPE} instead. + * + * <p>If the class has not yet been loaded, it is loaded first, using the given class loader. + * If the class has not yet been initialized and {@code shouldInitialize} is true, + * the class will be initialized. + * + * @throws ClassNotFoundException + * if the requested class can not be found. + * @throws LinkageError + * if an error occurs during linkage + * @throws ExceptionInInitializerError + * if an exception occurs during static initialization of a + * class. + */ + public static Class<?> forName(String className, boolean shouldInitialize, + ClassLoader classLoader) throws ClassNotFoundException { + + if (classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + // Catch an Exception thrown by the underlying native code. It wraps + // up everything inside a ClassNotFoundException, even if e.g. an + // Error occurred during initialization. This as a workaround for + // an ExceptionInInitializerError that's also wrapped. It is actually + // expected to be thrown. Maybe the same goes for other errors. + // Not wrapping up all the errors will break android though. + Class<?> result; + try { + result = classForName(className, shouldInitialize, + classLoader); + } catch (ClassNotFoundException e) { + Throwable cause = e.getCause(); + if (cause instanceof ExceptionInInitializerError) { + throw (ExceptionInInitializerError) cause; + } + throw e; + } + return result; + } + + private static native Class<?> classForName(String className, boolean shouldInitialize, + ClassLoader classLoader) throws ClassNotFoundException; + + /** + * Returns an array containing {@code Class} objects for all public classes + * and interfaces that are members of this class. This includes public + * members inherited from super classes and interfaces. If there are no such + * class members or if this object represents a primitive type then an array + * of length 0 is returned. + */ + public Class<?>[] getClasses() { + Class<?>[] result = getDeclaredClasses(this, true); + // Traverse all superclasses. + for (Class<?> c = this.getSuperclass(); c != null; c = c.getSuperclass()) { + Class<?>[] temp = getDeclaredClasses(c, true); + if (temp.length != 0) { + result = arraycopy(new Class[result.length + temp.length], result, temp); + } + } + return result; + } + + @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + + A annotation = getDeclaredAnnotation(annotationType); + if (annotation != null) { + return annotation; + } + + if (annotationType.isAnnotationPresent(Inherited.class)) { + for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) { + annotation = sup.getDeclaredAnnotation(annotationType); + if (annotation != null) { + return annotation; + } + } + } + + return null; + } + + /** + * Returns an array containing all the annotations of this class. If there are no annotations + * then an empty array is returned. + * + * @see #getDeclaredAnnotations() + */ + public Annotation[] getAnnotations() { + /* + * We need to get the annotations declared on this class, plus the + * annotations from superclasses that have the "@Inherited" annotation + * set. We create a temporary map to use while we accumulate the + * annotations and convert it to an array at the end. + * + * It's possible to have duplicates when annotations are inherited. + * We use a Map to filter those out. + * + * HashMap might be overkill here. + */ + HashMap<Class, Annotation> map = new HashMap<Class, Annotation>(); + Annotation[] declaredAnnotations = getDeclaredAnnotations(); + + for (int i = declaredAnnotations.length-1; i >= 0; --i) { + map.put(declaredAnnotations[i].annotationType(), declaredAnnotations[i]); + } + for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) { + declaredAnnotations = sup.getDeclaredAnnotations(); + for (int i = declaredAnnotations.length-1; i >= 0; --i) { + Class<?> clazz = declaredAnnotations[i].annotationType(); + if (!map.containsKey(clazz) && clazz.isAnnotationPresent(Inherited.class)) { + map.put(clazz, declaredAnnotations[i]); + } + } + } + + /* convert annotation values from HashMap to array */ + Collection<Annotation> coll = map.values(); + return coll.toArray(new Annotation[coll.size()]); + } + + /** + * Returns the canonical name of this class. If this class does not have a + * canonical name as defined in the Java Language Specification, then the + * method returns {@code null}. + */ + public String getCanonicalName() { + if (isLocalClass() || isAnonymousClass()) + return null; + + if (isArray()) { + /* + * The canonical name of an array type depends on the (existence of) + * the component type's canonical name. + */ + String name = getComponentType().getCanonicalName(); + if (name != null) { + return name + "[]"; + } + } else if (isMemberClass()) { + /* + * The canonical name of an inner class depends on the (existence + * of) the declaring class' canonical name. + */ + String name = getDeclaringClass().getCanonicalName(); + if (name != null) { + return name + "." + getSimpleName(); + } + } else { + /* + * The canonical name of a top-level class or primitive type is + * equal to the fully qualified name. + */ + return getName(); + } + + /* + * Other classes don't have a canonical name. + */ + return null; + } + + /** + * Returns the class loader which was used to load the class represented by + * this {@code Class}. Implementations are free to return {@code null} for + * classes that were loaded by the bootstrap class loader. The Android + * reference implementation, though, always returns a reference to an actual + * class loader. + */ + public ClassLoader getClassLoader() { + if (this.isPrimitive()) { + return null; + } + + ClassLoader loader = getClassLoaderImpl(); + if (loader == null) { + loader = BootClassLoader.getInstance(); + } + return loader; + } + + /** + * This must be provided by the VM vendor, as it is used by other provided + * class implementations in this package. Outside of this class, it is used + * by SecurityManager.classLoaderDepth(), + * currentClassLoader() and currentLoadedClass(). Return the ClassLoader for + * this Class without doing any security checks. The bootstrap ClassLoader + * is returned, unlike getClassLoader() which returns null in place of the + * bootstrap ClassLoader. + */ + ClassLoader getClassLoaderImpl() { + ClassLoader loader = getClassLoader(this); + return loader == null ? BootClassLoader.getInstance() : loader; + } + + /* + * Returns the defining class loader for the given class. + */ + private static native ClassLoader getClassLoader(Class<?> c); + + /** + * Returns a {@code Class} object which represents the component type if + * this class represents an array type. Returns {@code null} if this class + * does not represent an array type. The component type of an array type is + * the type of the elements of the array. + */ + public native Class<?> getComponentType(); + + /** + * Returns a {@code Constructor} object which represents the public + * constructor matching the given parameter types. + * {@code (Class[]) null} is equivalent to the empty array. + * + * <p>See {@link #getMethod} for details of the search order. + * Use {@link #getDeclaredConstructor} if you don't want to search superclasses. + * + * @throws NoSuchMethodException + * if the constructor can not be found. + */ + @SuppressWarnings("unchecked") + public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException { + return (Constructor) getConstructorOrMethod("<init>", false, true, parameterTypes); + } + + /** + * Returns a constructor or method with the given name. Use "<init>" to return a constructor. + */ + private Member getConstructorOrMethod(String name, boolean searchSuperTypes, + boolean publicOnly, Class<?>[] parameterTypes) throws NoSuchMethodException { + if (searchSuperTypes && !publicOnly) { + throw new AssertionError(); // can't lookup non-public members recursively + } + if (name == null) { + throw new NullPointerException("name == null"); + } + if (parameterTypes == null) { + parameterTypes = EmptyArray.CLASS; + } + for (Class<?> c : parameterTypes) { + if (c == null) { + throw new NoSuchMethodException("parameter type is null"); + } + } + Member result = searchSuperTypes + ? getPublicConstructorOrMethodRecursive(name, parameterTypes) + : Class.getDeclaredConstructorOrMethod(this, name, parameterTypes); + if (result == null || publicOnly && (result.getModifiers() & Modifier.PUBLIC) == 0) { + throw new NoSuchMethodException(name + " " + Arrays.toString(parameterTypes)); + } + return result; + } + + private Member getPublicConstructorOrMethodRecursive(String name, Class<?>[] parameterTypes) { + // search superclasses + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + Member result = Class.getDeclaredConstructorOrMethod(c, name, parameterTypes); + if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) { + return result; + } + } + + // search implemented interfaces + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + for (Class<?> ifc : c.getInterfaces()) { + Member result = ifc.getPublicConstructorOrMethodRecursive(name, parameterTypes); + if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) { + return result; + } + } + } + + return null; + } + + /** + * Returns an array containing {@code Constructor} objects for all public + * constructors for this {@code Class}. If there + * are no public constructors or if this {@code Class} represents an array + * class, a primitive type or void then an empty array is returned. + * + * @see #getDeclaredConstructors() + */ + public Constructor<?>[] getConstructors() { + return getDeclaredConstructors(this, true); + } + + /** + * Returns the annotations that are directly defined on the class + * represented by this {@code Class}. Annotations that are inherited are not + * included in the result. If there are no annotations at all, an empty + * array is returned. + * + * @see #getAnnotations() + */ + public native Annotation[] getDeclaredAnnotations(); + + /** + * Returns the annotation if it exists. + */ + native private <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass); + + /** + * Returns true if the annotation exists. + */ + native private boolean isDeclaredAnnotationPresent(Class<? extends Annotation> annotationClass); + + /** + * Returns an array containing {@code Class} objects for all classes and + * interfaces that are declared as members of the class which this {@code + * Class} represents. If there are no classes or interfaces declared or if + * this class represents an array class, a primitive type or void, then an + * empty array is returned. + */ + public Class<?>[] getDeclaredClasses() { + return getDeclaredClasses(this, false); + } + + /* + * Returns the list of member classes of the given class. + * If no members exist, an empty array is returned. + */ + private static native Class<?>[] getDeclaredClasses(Class<?> c, boolean publicOnly); + + /** + * Returns a {@code Constructor} object which represents the constructor + * matching the given parameter types that is declared by the class + * represented by this {@code Class}. + * {@code (Class[]) null} is equivalent to the empty array. + * + * <p>Use {@link #getConstructor} if you want to search superclasses. + * + * @throws NoSuchMethodException + * if the requested constructor can not be found. + */ + @SuppressWarnings("unchecked") + public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) + throws NoSuchMethodException { + return (Constructor) getConstructorOrMethod("<init>", false, false, parameterTypes); + } + + /** + * Returns an array containing {@code Constructor} objects for all + * constructors declared in the class represented by this {@code Class}. If + * there are no constructors or if this {@code Class} represents an array + * class, a primitive type, or void then an empty array is returned. + * + * @see #getConstructors() + */ + public Constructor<?>[] getDeclaredConstructors() { + return getDeclaredConstructors(this, false); + } + + /* + * Returns the list of constructors. If no constructors exist, an empty array is returned. + */ + private static native <T> Constructor<T>[] getDeclaredConstructors(Class<T> c, + boolean publicOnly); + + /** + * Returns a {@code Field} object for the field with the given name + * which is declared in the class represented by this {@code Class}. + * + * @throws NoSuchFieldException if the requested field can not be found. + * @see #getField(String) + */ + public Field getDeclaredField(String name) throws NoSuchFieldException { + if (name == null) { + throw new NullPointerException("name == null"); + } + Field result = getDeclaredField(this, name); + if (result == null) { + throw new NoSuchFieldException(name); + } + return result; + } + + /** + * Returns an array containing {@code Field} objects for all fields declared + * in the class represented by this {@code Class}. If there are no fields or + * if this {@code Class} represents an array class, a primitive type or void + * then an empty array is returned. + * + * @see #getFields() + */ + public Field[] getDeclaredFields() { + return getDeclaredFields(this, false); + } + + /* + * Returns the list of fields without performing any security checks + * first. If no fields exist at all, an empty array is returned. + */ + static native Field[] getDeclaredFields(Class<?> c, boolean publicOnly); + + /** + * Returns the field if it is defined by {@code c}; null otherwise. This + * may return a non-public member. + */ + static native Field getDeclaredField(Class<?> c, String name); + + /** + * Returns a {@code Method} object which represents the method matching the + * given name and parameter types that is declared by the class + * represented by this {@code Class}. + * {@code (Class[]) null} is equivalent to the empty array. + * + * <p>See {@link #getMethod} if you want to search superclasses. + * + * @throws NoSuchMethodException + * if the requested method can not be found. + * @throws NullPointerException + * if {@code name} is {@code null}. + */ + public Method getDeclaredMethod(String name, Class<?>... parameterTypes) + throws NoSuchMethodException { + Member member = getConstructorOrMethod(name, false, false, parameterTypes); + if (member instanceof Constructor) { + throw new NoSuchMethodException(name); + } + return (Method) member; + } + + /** + * Returns an array containing {@code Method} objects for all methods + * declared in the class represented by this {@code Class}. If there are no + * methods or if this {@code Class} represents an array class, a primitive + * type or void then an empty array is returned. + * + * @see #getMethods() + */ + public Method[] getDeclaredMethods() { + return getDeclaredMethods(this, false); + } + + /** + * Returns the list of methods. If no methods exist, an empty array is returned. + */ + static native Method[] getDeclaredMethods(Class<?> c, boolean publicOnly); + + /** + * Returns the constructor or method if it is defined by {@code c}; null + * otherwise. This may return a non-public member. Use "<init>" to get a constructor. + */ + static native Member getDeclaredConstructorOrMethod(Class c, String name, Class[] args); + + /** + * Returns the declaring {@code Class} of this {@code Class}. Returns + * {@code null} if the class is not a member of another class or if this + * {@code Class} represents an array class, a primitive type, or void. + */ + public native Class<?> getDeclaringClass(); + + /** + * Returns the enclosing {@code Class} of this {@code Class}. If there is no + * enclosing class the method returns {@code null}. + */ + public native Class<?> getEnclosingClass(); + + /** + * Returns the enclosing {@code Constructor} of this {@code Class}, if it is an + * anonymous or local/automatic class; otherwise {@code null}. + */ + public native Constructor<?> getEnclosingConstructor(); + + /** + * Returns the enclosing {@code Method} of this {@code Class}, if it is an + * anonymous or local/automatic class; otherwise {@code null}. + */ + public native Method getEnclosingMethod(); + + /** + * Returns the {@code enum} constants associated with this {@code Class}. + * Returns {@code null} if this {@code Class} does not represent an {@code + * enum} type. + */ + @SuppressWarnings("unchecked") // we only cast after confirming that this class is an enum + public T[] getEnumConstants() { + if (!isEnum()) { + return null; + } + return (T[]) Enum.getSharedConstants((Class) this).clone(); + } + + /** + * Returns a {@code Field} object which represents the public field with the + * given name. This method first searches the class C represented by + * this {@code Class}, then the interfaces implemented by C and finally the + * superclasses of C. + * + * @throws NoSuchFieldException + * if the field can not be found. + * @see #getDeclaredField(String) + */ + public Field getField(String name) throws NoSuchFieldException { + if (name == null) { + throw new NullPointerException("name == null"); + } + Field result = getPublicFieldRecursive(name); + if (result == null) { + throw new NoSuchFieldException(name); + } + return result; + } + + private Field getPublicFieldRecursive(String name) { + // search superclasses + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + Field result = Class.getDeclaredField(c, name); + if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) { + return result; + } + } + + // search implemented interfaces + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + for (Class<?> ifc : c.getInterfaces()) { + Field result = ifc.getPublicFieldRecursive(name); + if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) { + return result; + } + } + } + + return null; + } + + /** + * Returns an array containing {@code Field} objects for all public fields + * for the class C represented by this {@code Class}. Fields may be declared + * in C, the interfaces it implements or in the superclasses of C. The + * elements in the returned array are in no particular order. + * + * <p>If there are no public fields or if this class represents an array class, + * a primitive type or {@code void} then an empty array is returned. + * + * @see #getDeclaredFields() + */ + public Field[] getFields() { + List<Field> fields = new ArrayList<Field>(); + getPublicFieldsRecursive(fields); + + /* + * The result may include duplicates when this class implements an interface + * through multiple paths. Remove those duplicates. + */ + CollectionUtils.removeDuplicates(fields, Field.ORDER_BY_NAME_AND_DECLARING_CLASS); + return fields.toArray(new Field[fields.size()]); + } + + /** + * Populates {@code result} with public fields defined by this class, its + * superclasses, and all implemented interfaces. + */ + private void getPublicFieldsRecursive(List<Field> result) { + // search superclasses + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + for (Field field : Class.getDeclaredFields(c, true)) { + result.add(field); + } + } + + // search implemented interfaces + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + for (Class<?> ifc : c.getInterfaces()) { + ifc.getPublicFieldsRecursive(result); + } + } + } + + /** + * Returns the {@link Type}s of the interfaces that this {@code Class} directly + * implements. If the {@code Class} represents a primitive type or {@code + * void} then an empty array is returned. + */ + public Type[] getGenericInterfaces() { + Type[] result; + synchronized (Caches.genericInterfaces) { + result = Caches.genericInterfaces.get(this); + if (result == null) { + String annotationSignature = AnnotationAccess.getSignature(this); + if (annotationSignature == null) { + result = getInterfaces(); + } else { + GenericSignatureParser parser = new GenericSignatureParser(getClassLoader()); + parser.parseForClass(this, annotationSignature); + result = Types.getTypeArray(parser.interfaceTypes, false); + } + Caches.genericInterfaces.put(this, result); + } + } + return (result.length == 0) ? result : result.clone(); + } + + /** + * Returns the {@code Type} that represents the superclass of this {@code + * class}. + */ + public Type getGenericSuperclass() { + Type genericSuperclass = getSuperclass(); + // This method is specified to return null for all cases where getSuperclass + // returns null, i.e, for primitives, interfaces, void and java.lang.Object. + + if (genericSuperclass == null) { + return null; + } + String annotationSignature = AnnotationAccess.getSignature(this); + if (annotationSignature != null) { + GenericSignatureParser parser = new GenericSignatureParser(getClassLoader()); + parser.parseForClass(this, annotationSignature); + genericSuperclass = parser.superclassType; + } + return Types.getType(genericSuperclass); + } + + /** + * Returns an array of {@code Class} objects that match the interfaces + * in the {@code implements} declaration of the class represented + * by this {@code Class}. The order of the elements in the array is + * identical to the order in the original class declaration. If the class + * does not implement any interfaces, an empty array is returned. + */ + public native Class<?>[] getInterfaces(); + + /** + * Returns a {@code Method} object which represents the public method with + * the given name and parameter types. + * {@code (Class[]) null} is equivalent to the empty array. + * + * <p>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 its superclasses. + * + * <p>Use {@link #getDeclaredMethod} if you don't want to search superclasses. + * + * @throws NoSuchMethodException + * if the method can not be found. + */ + public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException { + Member member = getConstructorOrMethod(name, true, true, parameterTypes); + if (member instanceof Constructor) { + throw new NoSuchMethodException(name); + } + return (Method) member; + } + + /** + * Returns an array containing {@code Method} objects for all public methods + * for the class C represented by this {@code Class}. Methods may be + * declared in C, the interfaces it implements or in the superclasses of C. + * The elements in the returned array are in no particular order. + * + * <p>If there are no public methods or if this {@code Class} represents a + * primitive type or {@code void} then an empty array is returned. + * + * @see #getDeclaredMethods() + */ + public Method[] getMethods() { + List<Method> methods = new ArrayList<Method>(); + getPublicMethodsRecursive(methods); + + /* + * Remove methods defined by multiple types, preferring to keep methods + * declared by derived types. + */ + CollectionUtils.removeDuplicates(methods, Method.ORDER_BY_SIGNATURE); + return methods.toArray(new Method[methods.size()]); + } + + /** + * Populates {@code result} with public methods defined by this class, its + * superclasses, and all implemented interfaces, including overridden methods. + */ + private void getPublicMethodsRecursive(List<Method> result) { + // search superclasses + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + for (Method method : Class.getDeclaredMethods(c, true)) { + result.add(method); + } + } + + // search implemented interfaces + for (Class<?> c = this; c != null; c = c.getSuperclass()) { + for (Class<?> ifc : c.getInterfaces()) { + ifc.getPublicMethodsRecursive(result); + } + } + } + + /** + * Returns an integer that represents the modifiers of the class represented + * by this {@code Class}. The returned value is a combination of bits + * defined by constants in the {@link Modifier} class. + */ + public int getModifiers() { + return getModifiers(this, false); + } + + /* + * Returns the modifiers for the given class. + * + * {@code ignoreInnerClassesAttrib} determines whether we look for and use the + * flags from an "inner class" attribute + */ + private static native int getModifiers(Class<?> clazz, boolean ignoreInnerClassesAttrib); + + /** + * Returns the name of the class represented by this {@code Class}. For a + * description of the format which is used, see the class definition of + * {@link Class}. + */ + public String getName() { + String result = name; + return (result == null) ? (name = getNameNative()) : result; + } + + private native String getNameNative(); + + /** + * Returns the simple name of the class represented by this {@code Class} as + * defined in the source code. If there is no name (that is, the class is + * anonymous) then an empty string is returned. If the receiver is an array + * then the name of the underlying type with square braces appended (for + * example {@code "Integer[]"}) is returned. + * + * @return the simple name of the class represented by this {@code Class}. + */ + public String getSimpleName() { + if (isArray()) { + return getComponentType().getSimpleName() + "[]"; + } + + String name = getName(); + + if (isAnonymousClass()) { + return ""; + } + + if (isMemberClass() || isLocalClass()) { + return getInnerClassName(); + } + + int dot = name.lastIndexOf('.'); + if (dot != -1) { + return name.substring(dot + 1); + } + + return name; + } + + /* + * Returns the simple name of a member or local class, or null otherwise. + */ + private native String getInnerClassName(); + + /** + * Returns null. + */ + public ProtectionDomain getProtectionDomain() { + return null; + } + + /** + * Returns the URL of the given resource, or null if the resource is not found. + * The mapping between the resource name and the URL is managed by the class' class loader. + * + * @see ClassLoader + */ + public URL getResource(String resourceName) { + // Get absolute resource name, but without the leading slash + if (resourceName.startsWith("/")) { + resourceName = resourceName.substring(1); + } else { + String pkg = getName(); + int dot = pkg.lastIndexOf('.'); + if (dot != -1) { + pkg = pkg.substring(0, dot).replace('.', '/'); + } else { + pkg = ""; + } + + resourceName = pkg + "/" + resourceName; + } + + // Delegate to proper class loader + ClassLoader loader = getClassLoader(); + if (loader != null) { + return loader.getResource(resourceName); + } else { + return ClassLoader.getSystemResource(resourceName); + } + } + + /** + * Returns a read-only stream for the contents of the given resource, or null if the resource + * is not found. + * The mapping between the resource name and the stream is managed by the class' class loader. + * + * @see ClassLoader + */ + public InputStream getResourceAsStream(String resourceName) { + // Get absolute resource name, but without the leading slash + if (resourceName.startsWith("/")) { + resourceName = resourceName.substring(1); + } else { + String pkg = getName(); + int dot = pkg.lastIndexOf('.'); + if (dot != -1) { + pkg = pkg.substring(0, dot).replace('.', '/'); + } else { + pkg = ""; + } + + resourceName = pkg + "/" + resourceName; + } + + // Delegate to proper class loader + ClassLoader loader = getClassLoader(); + if (loader != null) { + return loader.getResourceAsStream(resourceName); + } else { + return ClassLoader.getSystemResourceAsStream(resourceName); + } + } + + /** + * Returns null. (On Android, a {@code ClassLoader} can load classes from multiple dex files. + * All classes from any given dex file will have the same signers, but different dex + * files may have different signers. This does not fit well with the original + * {@code ClassLoader}-based model of {@code getSigners}.) + */ + public Object[] getSigners() { + // See http://code.google.com/p/android/issues/detail?id=1766. + return null; + } + + /** + * Returns the {@code Class} object which represents the superclass of the + * class represented by this {@code Class}. If this {@code Class} represents + * the {@code Object} class, a primitive type, an interface or void then the + * method returns {@code null}. If this {@code Class} represents an array + * class then the {@code Object} class is returned. + */ + public native Class<? super T> getSuperclass(); + + /** + * Returns an array containing {@code TypeVariable} objects for type + * variables declared by the generic class represented by this {@code + * Class}. Returns an empty array if the class is not generic. + */ + @SuppressWarnings("unchecked") + public synchronized TypeVariable<Class<T>>[] getTypeParameters() { + String annotationSignature = AnnotationAccess.getSignature(this); + if (annotationSignature == null) { + return EmptyArray.TYPE_VARIABLE; + } + GenericSignatureParser parser = new GenericSignatureParser(getClassLoader()); + parser.parseForClass(this, annotationSignature); + return parser.formalTypeParameters; + } + + /** + * Tests whether this {@code Class} represents an annotation class. + */ + public boolean isAnnotation() { + final int ACC_ANNOTATION = 0x2000; // not public in reflect.Modifiers + int mod = getModifiers(this, true); + return (mod & ACC_ANNOTATION) != 0; + } + + @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + + if (isDeclaredAnnotationPresent(annotationType)) { + return true; + } + + if (annotationType.isDeclaredAnnotationPresent(Inherited.class)) { + for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) { + if (sup.isDeclaredAnnotationPresent(annotationType)) { + return true; + } + } + } + + return false; + } + + /** + * Tests whether the class represented by this {@code Class} is + * anonymous. + */ + native public boolean isAnonymousClass(); + + /** + * Tests whether the class represented by this {@code Class} is an array class. + */ + public boolean isArray() { + return getComponentType() != null; + } + + /** + * Tests whether the given class type can be converted to the class + * represented by this {@code Class}. Conversion may be done via an identity + * conversion or a widening reference conversion (if either the receiver or + * the argument represent primitive types, only the identity conversion + * applies). + * + * @throws NullPointerException + * if {@code c} is {@code null}. + */ + public native boolean isAssignableFrom(Class<?> c); + + /** + * Tests whether the class represented by this {@code Class} is an + * {@code enum}. + */ + public boolean isEnum() { + if (getSuperclass() != Enum.class) { + return false; + } + int mod = getModifiers(this, true); + return (mod & 0x4000) != 0; + } + + /** + * Tests whether the given object can be cast to the class + * represented by this {@code Class}. This is the runtime version of the + * {@code instanceof} operator. + * + * @return {@code true} if {@code object} can be cast to the type + * represented by this {@code Class}; {@code false} if {@code + * object} is {@code null} or cannot be cast. + */ + public native boolean isInstance(Object object); + + /** + * Tests whether this {@code Class} represents an interface. + */ + public native boolean isInterface(); + + /** + * Tests whether the class represented by this {@code Class} is defined + * locally. + */ + public boolean isLocalClass() { + boolean enclosed = (getEnclosingMethod() != null || + getEnclosingConstructor() != null); + return enclosed && !isAnonymousClass(); + } + + /** + * Tests whether the class represented by this {@code Class} is a member + * class. + */ + public boolean isMemberClass() { + return getDeclaringClass() != null; + } + + /** + * Tests whether this {@code Class} represents a primitive type. + */ + public native boolean isPrimitive(); + + /** + * Tests whether this {@code Class} represents a synthetic type. + */ + public boolean isSynthetic() { + final int ACC_SYNTHETIC = 0x1000; // not public in reflect.Modifiers + int mod = getModifiers(this, true); + return (mod & ACC_SYNTHETIC) != 0; + } + + /** + * Returns a new instance of the class represented by this {@code Class}, + * created by invoking the default (that is, zero-argument) constructor. If + * there is no such constructor, or if the creation fails (either because of + * a lack of available memory or because an exception is thrown by the + * constructor), an {@code InstantiationException} is thrown. If the default + * constructor exists but is not accessible from the context where this + * method is invoked, an {@code IllegalAccessException} is thrown. + * + * @throws IllegalAccessException + * if the default constructor is not visible. + * @throws InstantiationException + * if the instance can not be created. + */ + public T newInstance() throws InstantiationException, IllegalAccessException { + return newInstanceImpl(); + } + + private native T newInstanceImpl() throws IllegalAccessException, InstantiationException; + + @Override + public String toString() { + if (isPrimitive()) { + return getSimpleName(); + } + return (isInterface() ? "interface " : "class ") + getName(); + } + + /** + * Returns the {@code Package} of which the class represented by this + * {@code Class} is a member. Returns {@code null} if no {@code Package} + * object was created by the class loader of the class. + */ + public Package getPackage() { + // TODO This might be a hack, but the VM doesn't have the necessary info. + ClassLoader loader = getClassLoader(); + if (loader != null) { + String name = getName(); + int dot = name.lastIndexOf('.'); + return (dot != -1 ? loader.getPackage(name.substring(0, dot)) : null); + } + return null; + } + + /** + * Returns the assertion status for the class represented by this {@code + * Class}. Assertion is enabled / disabled based on the class loader, + * package or class default at runtime. + */ + public native boolean desiredAssertionStatus(); + + /** + * Casts this {@code Class} to represent a subclass of the given class. + * If successful, this {@code Class} is returned; otherwise a {@code + * ClassCastException} is thrown. + * + * @throws ClassCastException + * if this {@code Class} cannot be cast to the given type. + */ + @SuppressWarnings("unchecked") + public <U> Class<? extends U> asSubclass(Class<U> c) { + if (c.isAssignableFrom(this)) { + return (Class<? extends U>)this; + } + String actualClassName = this.getName(); + String desiredClassName = c.getName(); + throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName); + } + + /** + * Casts the given object to the type represented by this {@code Class}. + * If the object is {@code null} then the result is also {@code null}. + * + * @throws ClassCastException + * if the object cannot be cast to the given type. + */ + @SuppressWarnings("unchecked") + public T cast(Object obj) { + if (obj == null) { + return null; + } else if (this.isInstance(obj)) { + return (T)obj; + } + String actualClassName = obj.getClass().getName(); + String desiredClassName = this.getName(); + throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName); + } + + /** + * Copies two arrays into one. Assumes that the destination array is large + * enough. + * + * @param result the destination array + * @param head the first source array + * @param tail the second source array + * @return the destination array, that is, result + */ + private static <T extends Object> T[] arraycopy(T[] result, T[] head, T[] tail) { + System.arraycopy(head, 0, result, 0, head.length); + System.arraycopy(tail, 0, result, head.length, tail.length); + return result; + } + + /** + * The annotation directory offset of this class in its own Dex, or 0 if it + * is unknown. + * + * TODO: 0 is a sentinel that means 'no annotations directory'; this should be -1 if unknown + * + * @hide + */ + public int getDexAnnotationDirectoryOffset() { + Dex dex = getDex(); + if (dex == null) { + return 0; + } + int classDefIndex = getDexClassDefIndex(); + if (classDefIndex < 0) { + return 0; + } + return dex.annotationDirectoryOffsetFromClassDefIndex(classDefIndex); + } + + + /** + * Returns a resolved type from the dex cache, computing the type from the dex file if + * necessary. + * TODO: use Dalvik's dex cache. + * @hide + */ + public Class<?> getDexCacheType(Dex dex, int typeIndex) { + String internalName = dex.typeNames().get(typeIndex); + return InternalNames.getClass(getClassLoader(), internalName); + } + + /** + * Returns a string from the dex cache, computing the string from the dex file if necessary. + * + * @hide + */ + public String getDexCacheString(Dex dex, int dexStringIndex) { + return dex.strings().get(dexStringIndex); + } + + + private static class Caches { + /** + * Cache to avoid frequent recalculation of generic interfaces, which is generally uncommon. + * Sized sufficient to allow ConcurrentHashMapTest to run without recalculating its generic + * interfaces (required to avoid time outs). Validated by running reflection heavy code + * such as applications using Guice-like frameworks. + */ + private static final BasicLruCache<Class, Type[]> genericInterfaces + = new BasicLruCache<Class, Type[]>(8); + } +} diff --git a/libdvm/src/main/java/java/lang/ClassLoader.java b/libdvm/src/main/java/java/lang/ClassLoader.java new file mode 100644 index 0000000..c6a8091 --- /dev/null +++ b/libdvm/src/main/java/java/lang/ClassLoader.java @@ -0,0 +1,836 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +import dalvik.system.PathClassLoader; +import dalvik.system.VMStack; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.ByteBuffer; +import java.security.ProtectionDomain; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * Loads classes and resources from a repository. One or more class loaders are + * installed at runtime. These are consulted whenever the runtime system needs a + * specific class that is not yet available in-memory. Typically, class loaders + * are grouped into a tree where child class loaders delegate all requests to + * parent class loaders. Only if the parent class loader cannot satisfy the + * request, the child class loader itself tries to handle it. + * <p> + * {@code ClassLoader} is an abstract class that implements the common + * infrastructure required by all class loaders. Android provides several + * concrete implementations of the class, with + * {@link dalvik.system.PathClassLoader} being the one typically used. Other + * applications may implement subclasses of {@code ClassLoader} to provide + * special ways for loading classes. + * </p> + * @see Class + */ +public abstract class ClassLoader { + + /** + * The 'System' ClassLoader - the one that is responsible for loading + * classes from the classpath. It is not equal to the bootstrap class loader - + * that one handles the built-in classes. + * + * Because of a potential class initialization race between ClassLoader and + * java.lang.System, reproducible when using JDWP with "suspend=y", we defer + * creation of the system class loader until first use. We use a static + * inner class to get synchronization at init time without having to sync on + * every access. + * + * @see #getSystemClassLoader() + */ + static private class SystemClassLoader { + public static ClassLoader loader = ClassLoader.createSystemClassLoader(); + } + + /** + * The parent ClassLoader. + */ + private ClassLoader parent; + + /** + * The packages known to the class loader. + */ + private Map<String, Package> packages = new HashMap<String, Package>(); + + /** + * Create the system class loader. Note this is NOT the bootstrap class + * loader (which is managed by the VM). We use a null value for the parent + * to indicate that the bootstrap loader is our parent. + */ + private static ClassLoader createSystemClassLoader() { + String classPath = System.getProperty("java.class.path", "."); + + // String[] paths = classPath.split(":"); + // URL[] urls = new URL[paths.length]; + // for (int i = 0; i < paths.length; i++) { + // try { + // urls[i] = new URL("file://" + paths[i]); + // } + // catch (Exception ex) { + // ex.printStackTrace(); + // } + // } + // + // return new java.net.URLClassLoader(urls, null); + + // TODO Make this a java.net.URLClassLoader once we have those? + return new PathClassLoader(classPath, BootClassLoader.getInstance()); + } + + /** + * Returns the system class loader. This is the parent for new + * {@code ClassLoader} instances and is typically the class loader used to + * start the application. + */ + public static ClassLoader getSystemClassLoader() { + return SystemClassLoader.loader; + } + + /** + * Finds the URL of the resource with the specified name. The system class + * loader's resource lookup algorithm is used to find the resource. + * + * @return the {@code URL} object for the requested resource or {@code null} + * if the resource can not be found. + * @param resName + * the name of the resource to find. + * @see Class#getResource + */ + public static URL getSystemResource(String resName) { + return SystemClassLoader.loader.getResource(resName); + } + + /** + * Returns an enumeration of URLs for the resource with the specified name. + * The system class loader's resource lookup algorithm is used to find the + * resource. + * + * @return an enumeration of {@code URL} objects containing the requested + * resources. + * @param resName + * the name of the resource to find. + * @throws IOException + * if an I/O error occurs. + */ + public static Enumeration<URL> getSystemResources(String resName) throws IOException { + return SystemClassLoader.loader.getResources(resName); + } + + /** + * Returns a stream for the resource with the specified name. The system + * class loader's resource lookup algorithm is used to find the resource. + * Basically, the contents of the java.class.path are searched in order, + * looking for a path which matches the specified resource. + * + * @return a stream for the resource or {@code null}. + * @param resName + * the name of the resource to find. + * @see Class#getResourceAsStream + */ + public static InputStream getSystemResourceAsStream(String resName) { + return SystemClassLoader.loader.getResourceAsStream(resName); + } + + /** + * Constructs a new instance of this class with the system class loader as + * its parent. + */ + protected ClassLoader() { + this(getSystemClassLoader(), false); + } + + /** + * Constructs a new instance of this class with the specified class loader + * as its parent. + * + * @param parentLoader + * The {@code ClassLoader} to use as the new class loader's + * parent. + */ + protected ClassLoader(ClassLoader parentLoader) { + this(parentLoader, false); + } + + /* + * constructor for the BootClassLoader which needs parent to be null. + */ + ClassLoader(ClassLoader parentLoader, boolean nullAllowed) { + if (parentLoader == null && !nullAllowed) { + throw new NullPointerException("parentLoader == null && !nullAllowed"); + } + parent = parentLoader; + } + + /** + * Constructs a new class from an array of bytes containing a class + * definition in class file format. + * + * @param classRep + * the memory image of a class file. + * @param offset + * the offset into {@code classRep}. + * @param length + * the length of the class file. + * @return the {@code Class} object created from the specified subset of + * data in {@code classRep}. + * @throws ClassFormatError + * if {@code classRep} does not contain a valid class. + * @throws IndexOutOfBoundsException + * if {@code offset < 0}, {@code length < 0} or if + * {@code offset + length} is greater than the length of + * {@code classRep}. + * @deprecated Use {@link #defineClass(String, byte[], int, int)} instead. + */ + @Deprecated + protected final Class<?> defineClass(byte[] classRep, int offset, int length) + throws ClassFormatError { + throw new UnsupportedOperationException("can't load this type of class file"); + } + + /** + * Constructs a new class from an array of bytes containing a class + * definition in class file format. + * + * @param className + * the expected name of the new class, may be {@code null} if not + * known. + * @param classRep + * the memory image of a class file. + * @param offset + * the offset into {@code classRep}. + * @param length + * the length of the class file. + * @return the {@code Class} object created from the specified subset of + * data in {@code classRep}. + * @throws ClassFormatError + * if {@code classRep} does not contain a valid class. + * @throws IndexOutOfBoundsException + * if {@code offset < 0}, {@code length < 0} or if + * {@code offset + length} is greater than the length of + * {@code classRep}. + */ + protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length) + throws ClassFormatError { + throw new UnsupportedOperationException("can't load this type of class file"); + } + + /** + * Constructs a new class from an array of bytes containing a class + * definition in class file format and assigns the specified protection + * domain to the new class. If the provided protection domain is + * {@code null} then a default protection domain is assigned to the class. + * + * @param className + * the expected name of the new class, may be {@code null} if not + * known. + * @param classRep + * the memory image of a class file. + * @param offset + * the offset into {@code classRep}. + * @param length + * the length of the class file. + * @param protectionDomain + * the protection domain to assign to the loaded class, may be + * {@code null}. + * @return the {@code Class} object created from the specified subset of + * data in {@code classRep}. + * @throws ClassFormatError + * if {@code classRep} does not contain a valid class. + * @throws IndexOutOfBoundsException + * if {@code offset < 0}, {@code length < 0} or if + * {@code offset + length} is greater than the length of + * {@code classRep}. + * @throws NoClassDefFoundError + * if {@code className} is not equal to the name of the class + * contained in {@code classRep}. + */ + protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length, + ProtectionDomain protectionDomain) throws java.lang.ClassFormatError { + throw new UnsupportedOperationException("can't load this type of class file"); + } + + /** + * Defines a new class with the specified name, byte code from the byte + * buffer and the optional protection domain. If the provided protection + * domain is {@code null} then a default protection domain is assigned to + * the class. + * + * @param name + * the expected name of the new class, may be {@code null} if not + * known. + * @param b + * the byte buffer containing the byte code of the new class. + * @param protectionDomain + * the protection domain to assign to the loaded class, may be + * {@code null}. + * @return the {@code Class} object created from the data in {@code b}. + * @throws ClassFormatError + * if {@code b} does not contain a valid class. + * @throws NoClassDefFoundError + * if {@code className} is not equal to the name of the class + * contained in {@code b}. + */ + protected final Class<?> defineClass(String name, ByteBuffer b, + ProtectionDomain protectionDomain) throws ClassFormatError { + + byte[] temp = new byte[b.remaining()]; + b.get(temp); + return defineClass(name, temp, 0, temp.length, protectionDomain); + } + + /** + * Overridden by subclasses, throws a {@code ClassNotFoundException} by + * default. This method is called by {@code loadClass} after the parent + * {@code ClassLoader} has failed to find a loaded class of the same name. + * + * @param className + * the name of the class to look for. + * @return the {@code Class} object that is found. + * @throws ClassNotFoundException + * if the class cannot be found. + */ + protected Class<?> findClass(String className) throws ClassNotFoundException { + throw new ClassNotFoundException(className); + } + + /** + * Returns the class with the specified name if it has already been loaded + * by the VM or {@code null} if it has not yet been loaded. + * + * @param className + * the name of the class to look for. + * @return the {@code Class} object or {@code null} if the requested class + * has not been loaded. + */ + protected final Class<?> findLoadedClass(String className) { + ClassLoader loader; + if (this == BootClassLoader.getInstance()) + loader = null; + else + loader = this; + return VMClassLoader.findLoadedClass(loader, className); + } + + /** + * Finds the class with the specified name, loading it using the system + * class loader if necessary. + * + * @param className + * the name of the class to look for. + * @return the {@code Class} object with the requested {@code className}. + * @throws ClassNotFoundException + * if the class can not be found. + */ + protected final Class<?> findSystemClass(String className) throws ClassNotFoundException { + return Class.forName(className, false, getSystemClassLoader()); + } + + /** + * Returns this class loader's parent. + * + * @return this class loader's parent or {@code null}. + */ + public final ClassLoader getParent() { + return parent; + } + + /** + * Returns the URL of the resource with the specified name. This + * implementation first tries to use the parent class loader to find the + * resource; if this fails then {@link #findResource(String)} is called to + * find the requested resource. + * + * @param resName + * the name of the resource to find. + * @return the {@code URL} object for the requested resource or {@code null} + * if the resource can not be found + * @see Class#getResource + */ + public URL getResource(String resName) { + URL resource = parent.getResource(resName); + if (resource == null) { + resource = findResource(resName); + } + return resource; + } + + /** + * Returns an enumeration of URLs for the resource with the specified name. + * This implementation first uses this class loader's parent to find the + * resource, then it calls {@link #findResources(String)} to get additional + * URLs. The returned enumeration contains the {@code URL} objects of both + * find operations. + * + * @return an enumeration of {@code URL} objects for the requested resource. + * @param resName + * the name of the resource to find. + * @throws IOException + * if an I/O error occurs. + */ + @SuppressWarnings("unchecked") + public Enumeration<URL> getResources(String resName) throws IOException { + + Enumeration first = parent.getResources(resName); + Enumeration second = findResources(resName); + + return new TwoEnumerationsInOne(first, second); + } + + /** + * Returns a stream for the resource with the specified name. See + * {@link #getResource(String)} for a description of the lookup algorithm + * used to find the resource. + * + * @return a stream for the resource or {@code null} if the resource can not be found + * @param resName + * the name of the resource to find. + * @see Class#getResourceAsStream + */ + public InputStream getResourceAsStream(String resName) { + try { + URL url = getResource(resName); + if (url != null) { + return url.openStream(); + } + } catch (IOException ex) { + // Don't want to see the exception. + } + + return null; + } + + /** + * Loads the class with the specified name. Invoking this method is + * equivalent to calling {@code loadClass(className, false)}. + * <p> + * <strong>Note:</strong> In the Android reference implementation, the + * second parameter of {@link #loadClass(String, boolean)} is ignored + * anyway. + * </p> + * + * @return the {@code Class} object. + * @param className + * the name of the class to look for. + * @throws ClassNotFoundException + * if the class can not be found. + */ + public Class<?> loadClass(String className) throws ClassNotFoundException { + return loadClass(className, false); + } + + /** + * Loads the class with the specified name, optionally linking it after + * loading. The following steps are performed: + * <ol> + * <li> Call {@link #findLoadedClass(String)} to determine if the requested + * class has already been loaded.</li> + * <li>If the class has not yet been loaded: Invoke this method on the + * parent class loader.</li> + * <li>If the class has still not been loaded: Call + * {@link #findClass(String)} to find the class.</li> + * </ol> + * <p> + * <strong>Note:</strong> In the Android reference implementation, the + * {@code resolve} parameter is ignored; classes are never linked. + * </p> + * + * @return the {@code Class} object. + * @param className + * the name of the class to look for. + * @param resolve + * Indicates if the class should be resolved after loading. This + * parameter is ignored on the Android reference implementation; + * classes are not resolved. + * @throws ClassNotFoundException + * if the class can not be found. + */ + protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { + Class<?> clazz = findLoadedClass(className); + + if (clazz == null) { + try { + clazz = parent.loadClass(className, false); + } catch (ClassNotFoundException e) { + // Don't want to see this. + } + + if (clazz == null) { + clazz = findClass(className); + } + } + + return clazz; + } + + /** + * Forces a class to be linked (initialized). If the class has already been + * linked this operation has no effect. + * <p> + * <strong>Note:</strong> In the Android reference implementation, this + * method has no effect. + * </p> + * + * @param clazz + * the class to link. + */ + protected final void resolveClass(Class<?> clazz) { + // no-op, doesn't make sense on android. + } + + /** + * Finds the URL of the resource with the specified name. This + * implementation just returns {@code null}; it should be overridden in + * subclasses. + * + * @param resName + * the name of the resource to find. + * @return the {@code URL} object for the requested resource. + */ + protected URL findResource(String resName) { + return null; + } + + /** + * Finds an enumeration of URLs for the resource with the specified name. + * This implementation just returns an empty {@code Enumeration}; it should + * be overridden in subclasses. + * + * @param resName + * the name of the resource to find. + * @return an enumeration of {@code URL} objects for the requested resource. + * @throws IOException + * if an I/O error occurs. + */ + @SuppressWarnings( { + "unchecked", "unused" + }) + protected Enumeration<URL> findResources(String resName) throws IOException { + return Collections.emptyEnumeration(); + } + + /** + * Returns the absolute path of the native library with the specified name, + * or {@code null}. If this method returns {@code null} then the virtual + * machine searches the directories specified by the system property + * "java.library.path". + * <p> + * This implementation always returns {@code null}. + * </p> + * + * @param libName + * the name of the library to find. + * @return the absolute path of the library. + */ + protected String findLibrary(String libName) { + return null; + } + + /** + * Returns the package with the specified name. Package information is + * searched in this class loader. + * + * @param name + * the name of the package to find. + * @return the package with the requested name; {@code null} if the package + * can not be found. + */ + protected Package getPackage(String name) { + synchronized (packages) { + return packages.get(name); + } + } + + /** + * Returns all the packages known to this class loader. + * + * @return an array with all packages known to this class loader. + */ + protected Package[] getPackages() { + synchronized (packages) { + Collection<Package> col = packages.values(); + Package[] result = new Package[col.size()]; + col.toArray(result); + return result; + } + } + + /** + * Defines and returns a new {@code Package} using the specified + * information. If {@code sealBase} is {@code null}, the package is left + * unsealed. Otherwise, the package is sealed using this URL. + * + * @param name + * the name of the package. + * @param specTitle + * the title of the specification. + * @param specVersion + * the version of the specification. + * @param specVendor + * the vendor of the specification. + * @param implTitle + * the implementation title. + * @param implVersion + * the implementation version. + * @param implVendor + * the specification vendor. + * @param sealBase + * the URL used to seal this package or {@code null} to leave the + * package unsealed. + * @return the {@code Package} object that has been created. + * @throws IllegalArgumentException + * if a package with the specified name already exists. + */ + protected Package definePackage(String name, String specTitle, String specVersion, + String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) + throws IllegalArgumentException { + + synchronized (packages) { + if (packages.containsKey(name)) { + throw new IllegalArgumentException("Package " + name + " already defined"); + } + + Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle, + implVersion, implVendor, sealBase); + + packages.put(name, newPackage); + + return newPackage; + } + } + + /** + * Sets the signers of the specified class. This implementation does + * nothing. + * + * @param c + * the {@code Class} object for which to set the signers. + * @param signers + * the signers for {@code c}. + */ + protected final void setSigners(Class<?> c, Object[] signers) { + } + + /** + * Sets the assertion status of the class with the specified name. + * <p> + * <strong>Note: </strong>This method does nothing in the Android reference + * implementation. + * </p> + * + * @param cname + * the name of the class for which to set the assertion status. + * @param enable + * the new assertion status. + */ + public void setClassAssertionStatus(String cname, boolean enable) { + } + + /** + * Sets the assertion status of the package with the specified name. + * <p> + * <strong>Note: </strong>This method does nothing in the Android reference + * implementation. + * </p> + * + * @param pname + * the name of the package for which to set the assertion status. + * @param enable + * the new assertion status. + */ + public void setPackageAssertionStatus(String pname, boolean enable) { + } + + /** + * Sets the default assertion status for this class loader. + * <p> + * <strong>Note: </strong>This method does nothing in the Android reference + * implementation. + * </p> + * + * @param enable + * the new assertion status. + */ + public void setDefaultAssertionStatus(boolean enable) { + } + + /** + * Sets the default assertion status for this class loader to {@code false} + * and removes any package default and class assertion status settings. + * <p> + * <strong>Note:</strong> This method does nothing in the Android reference + * implementation. + * </p> + */ + public void clearAssertionStatus() { + } +} + +/* + * Provides a helper class that combines two existing URL enumerations into one. + * It is required for the getResources() methods. Items are fetched from the + * first enumeration until it's empty, then from the second one. + */ +class TwoEnumerationsInOne implements Enumeration<URL> { + + private Enumeration<URL> first; + + private Enumeration<URL> second; + + public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) { + this.first = first; + this.second = second; + } + + public boolean hasMoreElements() { + return first.hasMoreElements() || second.hasMoreElements(); + } + + public URL nextElement() { + if (first.hasMoreElements()) { + return first.nextElement(); + } else { + return second.nextElement(); + } + } + +} + +/** + * Provides an explicit representation of the boot class loader. It sits at the + * head of the class loader chain and delegates requests to the VM's internal + * class loading mechanism. + */ +class BootClassLoader extends ClassLoader { + + private static BootClassLoader instance; + + @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED") + public static synchronized BootClassLoader getInstance() { + if (instance == null) { + instance = new BootClassLoader(); + } + + return instance; + } + + public BootClassLoader() { + super(null, true); + } + + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + return VMClassLoader.loadClass(name, false); + } + + @Override + protected URL findResource(String name) { + return VMClassLoader.getResource(name); + } + + @SuppressWarnings("unused") + @Override + protected Enumeration<URL> findResources(String resName) throws IOException { + return Collections.enumeration(VMClassLoader.getResources(resName)); + } + + /** + * Returns package information for the given package. Unfortunately, the + * Android BootClassLoader doesn't really have this information, and as a + * non-secure ClassLoader, it isn't even required to, according to the spec. + * Yet, we want to provide it, in order to make all those hopeful callers of + * {@code myClass.getPackage().getName()} happy. Thus we construct a Package + * object the first time it is being requested and fill most of the fields + * with dummy values. The Package object is then put into the ClassLoader's + * Package cache, so we see the same one next time. We don't create Package + * objects for null arguments or for the default package. + * <p> + * There a limited chance that we end up with multiple Package objects + * representing the same package: It can happen when when a package is + * scattered across different JAR files being loaded by different + * ClassLoaders. Rather unlikely, and given that this whole thing is more or + * less a workaround, probably not worth the effort. + */ + @Override + protected Package getPackage(String name) { + if (name != null && !name.isEmpty()) { + synchronized (this) { + Package pack = super.getPackage(name); + + if (pack == null) { + pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0", + "Unknown", null); + } + + return pack; + } + } + + return null; + } + + @Override + public URL getResource(String resName) { + return findResource(resName); + } + + @Override + protected Class<?> loadClass(String className, boolean resolve) + throws ClassNotFoundException { + Class<?> clazz = findLoadedClass(className); + + if (clazz == null) { + clazz = findClass(className); + } + + return clazz; + } + + @Override + public Enumeration<URL> getResources(String resName) throws IOException { + return findResources(resName); + } +} + +/** + * TODO Open issues - Missing / empty methods - Signer stuff - Protection + * domains - Assertions + */ diff --git a/libdvm/src/main/java/java/lang/Daemons.java b/libdvm/src/main/java/java/lang/Daemons.java new file mode 100644 index 0000000..78a4152 --- /dev/null +++ b/libdvm/src/main/java/java/lang/Daemons.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +import dalvik.system.VMRuntime; +import java.lang.ref.FinalizerReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.TimeoutException; +import libcore.util.EmptyArray; + +/** + * Calls Object.finalize() on objects in the finalizer reference queue. The VM + * will abort if any finalize() call takes more than the maximum finalize time + * to complete. + * + * @hide + */ +public final class Daemons { + private static final int NANOS_PER_MILLI = 1000 * 1000; + private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000; + private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND; + + public static void start() { + ReferenceQueueDaemon.INSTANCE.start(); + FinalizerDaemon.INSTANCE.start(); + FinalizerWatchdogDaemon.INSTANCE.start(); + } + + public static void stop() { + ReferenceQueueDaemon.INSTANCE.stop(); + FinalizerDaemon.INSTANCE.stop(); + FinalizerWatchdogDaemon.INSTANCE.stop(); + } + + /** + * A background task that provides runtime support to the application. + * Daemons can be stopped and started, but only so that the zygote can be a + * single-threaded process when it forks. + */ + private static abstract class Daemon implements Runnable { + private Thread thread; + + public synchronized void start() { + if (thread != null) { + throw new IllegalStateException("already running"); + } + thread = new Thread(ThreadGroup.mSystem, this, + getClass().getSimpleName()); + thread.setDaemon(true); + thread.start(); + } + + public abstract void run(); + + /** + * Returns true while the current thread should continue to run; false + * when it should return. + */ + protected synchronized boolean isRunning() { + return thread != null; + } + + public synchronized void interrupt() { + if (thread == null) { + throw new IllegalStateException("not running"); + } + thread.interrupt(); + } + + /** + * Waits for the runtime thread to stop. This interrupts the thread + * currently running the runnable and then waits for it to exit. + */ + public void stop() { + Thread threadToStop; + synchronized (this) { + threadToStop = thread; + thread = null; + } + if (threadToStop == null) { + throw new IllegalStateException("not running"); + } + threadToStop.interrupt(); + while (true) { + try { + threadToStop.join(); + return; + } catch (InterruptedException ignored) { + } + } + } + + /** + * Returns the current stack trace of the thread, or an empty stack trace + * if the thread is not currently running. + */ + public synchronized StackTraceElement[] getStackTrace() { + return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT; + } + } + + /** + * This heap management thread moves elements from the garbage collector's + * pending list to the managed reference queue. + */ + private static class ReferenceQueueDaemon extends Daemon { + private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon(); + + @Override public void run() { + while (isRunning()) { + Reference<?> list; + try { + synchronized (ReferenceQueue.class) { + while (ReferenceQueue.unenqueued == null) { + ReferenceQueue.class.wait(); + } + list = ReferenceQueue.unenqueued; + ReferenceQueue.unenqueued = null; + } + } catch (InterruptedException e) { + continue; + } + enqueue(list); + } + } + + private void enqueue(Reference<?> list) { + while (list != null) { + Reference<?> reference; + // pendingNext is owned by the GC so no synchronization is required + if (list == list.pendingNext) { + reference = list; + reference.pendingNext = null; + list = null; + } else { + reference = list.pendingNext; + list.pendingNext = reference.pendingNext; + reference.pendingNext = null; + } + reference.enqueueInternal(); + } + } + } + + private static class FinalizerDaemon extends Daemon { + private static final FinalizerDaemon INSTANCE = new FinalizerDaemon(); + private final ReferenceQueue<Object> queue = FinalizerReference.queue; + private volatile Object finalizingObject; + private volatile long finalizingStartedNanos; + + @Override public void run() { + while (isRunning()) { + // Take a reference, blocking until one is ready or the thread should stop + try { + doFinalize((FinalizerReference<?>) queue.remove()); + } catch (InterruptedException ignored) { + } + } + } + + @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION") + private void doFinalize(FinalizerReference<?> reference) { + FinalizerReference.remove(reference); + Object object = reference.get(); + reference.clear(); + try { + finalizingStartedNanos = System.nanoTime(); + finalizingObject = object; + synchronized (FinalizerWatchdogDaemon.INSTANCE) { + FinalizerWatchdogDaemon.INSTANCE.notify(); + } + object.finalize(); + } catch (Throwable ex) { + // The RI silently swallows these, but Android has always logged. + System.logE("Uncaught exception thrown by finalizer", ex); + } finally { + finalizingObject = null; + } + } + } + + /** + * The watchdog exits the VM if the finalizer ever gets stuck. We consider + * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS + * on one instance. + */ + private static class FinalizerWatchdogDaemon extends Daemon { + private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon(); + + @Override public void run() { + while (isRunning()) { + Object object = waitForObject(); + if (object == null) { + // We have been interrupted, need to see if this daemon has been stopped. + continue; + } + boolean finalized = waitForFinalization(object); + if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) { + finalizerTimedOut(object); + break; + } + } + } + + private Object waitForObject() { + while (true) { + Object object = FinalizerDaemon.INSTANCE.finalizingObject; + if (object != null) { + return object; + } + synchronized (this) { + // wait until something is ready to be finalized + // http://code.google.com/p/android/issues/detail?id=22778 + try { + wait(); + } catch (InterruptedException e) { + // Daemon.stop may have interrupted us. + return null; + } + } + } + } + + private void sleepFor(long startNanos, long durationNanos) { + while (true) { + long elapsedNanos = System.nanoTime() - startNanos; + long sleepNanos = durationNanos - elapsedNanos; + long sleepMills = sleepNanos / NANOS_PER_MILLI; + if (sleepMills <= 0) { + return; + } + try { + Thread.sleep(sleepMills); + } catch (InterruptedException e) { + if (!isRunning()) { + return; + } + } + } + } + + private boolean waitForFinalization(Object object) { + sleepFor(FinalizerDaemon.INSTANCE.finalizingStartedNanos, MAX_FINALIZE_NANOS); + return object != FinalizerDaemon.INSTANCE.finalizingObject; + } + + private static void finalizerTimedOut(Object object) { + // The current object has exceeded the finalization deadline; abort! + String message = object.getClass().getName() + ".finalize() timed out after " + + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds"; + Exception syntheticException = new TimeoutException(message); + // We use the stack from where finalize() was running to show where it was stuck. + syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); + Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler(); + if (h == null) { + // If we have no handler, log and exit. + System.logE(message, syntheticException); + System.exit(2); + } + // Otherwise call the handler to do crash reporting. + // We don't just throw because we're not the thread that + // timed out; we're the thread that detected it. + h.uncaughtException(Thread.currentThread(), syntheticException); + } + } +} diff --git a/libdvm/src/main/java/java/lang/Enum.java b/libdvm/src/main/java/java/lang/Enum.java new file mode 100644 index 0000000..4b897aa --- /dev/null +++ b/libdvm/src/main/java/java/lang/Enum.java @@ -0,0 +1,221 @@ +/* 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; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import libcore.util.BasicLruCache; +import libcore.util.EmptyArray; + +/** + * The superclass of all enumerated types. Actual enumeration types inherit from + * this class, but extending this class does not make a class an enumeration + * type, since the compiler needs to generate special information for it. + */ +public abstract class Enum<E extends Enum<E>> implements Serializable, Comparable<E> { + + private static final long serialVersionUID = -4300926546619394005L; + + private static final BasicLruCache<Class<? extends Enum>, Object[]> sharedConstantsCache + = new BasicLruCache<Class<? extends Enum>, Object[]>(64) { + @Override protected Object[] create(Class<? extends Enum> enumType) { + if (!enumType.isEnum()) { + return null; + } + Method method = (Method) Class.getDeclaredConstructorOrMethod( + enumType, "values", EmptyArray.CLASS); + try { + return (Object[]) method.invoke((Object[]) null); + } catch (IllegalAccessException impossible) { + throw new AssertionError(); + } catch (InvocationTargetException impossible) { + throw new AssertionError(); + } + } + }; + + private final String name; + + private final int ordinal; + + /** + * Constructor for constants of enum subtypes. + * + * @param name + * the enum constant's declared name. + * @param ordinal + * the enum constant's ordinal, which corresponds to its position + * in the enum declaration, starting at zero. + */ + protected Enum(String name, int ordinal) { + this.name = name; + this.ordinal = ordinal; + } + + /** + * Returns the name of this enum constant. The name is the field as it + * appears in the {@code enum} declaration. + * + * @return the name of this enum constant. + * @see #toString() + */ + public final String name() { + return name; + } + + /** + * Returns the position of the enum constant in the declaration. The first + * constant has an ordinal value of zero. + * + * @return the ordinal value of this enum constant. + */ + public final int ordinal() { + return ordinal; + } + + /** + * Returns a string containing a concise, human-readable description of this + * object. In this case, the enum constant's name is returned. + * + * @return a printable representation of this object. + */ + @Override + public String toString() { + return name; + } + + /** + * Compares this object with the specified object and indicates if they are + * equal. In order to be equal, {@code object} must be identical to this + * enum constant. + * + * @param other + * the object to compare this enum constant with. + * @return {@code true} if the specified object is equal to this + * {@code Enum}; {@code false} otherwise. + */ + @Override + public final boolean equals(Object other) { + return this == other; + } + + @Override + public final int hashCode() { + return ordinal + (name == null ? 0 : name.hashCode()); + } + + /** + * {@code Enum} objects are singletons, they may not be cloned. This method + * always throws a {@code CloneNotSupportedException}. + * + * @return does not return. + * @throws CloneNotSupportedException + * is always thrown. + */ + @Override + protected final Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException("Enums may not be cloned"); + } + + /** + * Compares this object to the specified enum object to determine their + * relative order. This method compares the object's ordinal values, that + * is, their position in the enum declaration. + * + * @param o + * the enum object to compare this object to. + * @return a negative value if the ordinal value of this enum constant is + * less than the ordinal value of {@code o}; 0 if the ordinal + * values of this enum constant and {@code o} are equal; a positive + * value if the ordinal value of this enum constant is greater than + * the ordinal value of {@code o}. + * @see java.lang.Comparable + */ + public final int compareTo(E o) { + return ordinal - o.ordinal(); + } + + /** + * Returns the enum constant's declaring class. + * + * @return the class object representing the constant's enum type. + */ + @SuppressWarnings("unchecked") + public final Class<E> getDeclaringClass() { + Class<?> myClass = getClass(); + Class<?> mySuperClass = myClass.getSuperclass(); + if (Enum.class == mySuperClass) { + return (Class<E>)myClass; + } + return (Class<E>)mySuperClass; + } + + /** + * Returns the constant with the specified name of the specified enum type. + * + * @param enumType + * the class of the enumerated type to search for the constant + * value. + * @param name + * the name of the constant value to find. + * @return the enum constant. + * @throws NullPointerException + * if either {@code enumType} or {@code name} are {@code null}. + * @throws IllegalArgumentException + * if {@code enumType} is not an enumerated type or does not + * have a constant value called {@code name}. + */ + public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { + if (enumType == null) { + throw new NullPointerException("enumType == null"); + } else if (name == null) { + throw new NullPointerException("name == null"); + } + T[] values = getSharedConstants(enumType); + if (values == null) { + throw new IllegalArgumentException(enumType + " is not an enum type"); + } + for (T value : values) { + if (name.equals(value.name())) { + return value; + } + } + throw new IllegalArgumentException(name + " is not a constant in " + enumType.getName()); + } + + /** + * Returns a shared, mutable array containing the constants of this enum. It + * is an error to modify the returned array. + * + * @hide + */ + @SuppressWarnings("unchecked") // the cache always returns the type matching enumType + public static <T extends Enum<T>> T[] getSharedConstants(Class<T> enumType) { + return (T[]) sharedConstantsCache.get(enumType); + } + + /** + * Enum types may not have finalizers. + * + * @since 1.6 + */ + @Override + @SuppressWarnings("FinalizeDoesntCallSuperFinalize") + protected final void finalize() { + } +} diff --git a/libdvm/src/main/java/java/lang/Object.java b/libdvm/src/main/java/java/lang/Object.java new file mode 100644 index 0000000..d2cd2f1 --- /dev/null +++ b/libdvm/src/main/java/java/lang/Object.java @@ -0,0 +1,443 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +/** + * The root class of the Java class hierarchy. All non-primitive types + * (including arrays) inherit either directly or indirectly from this class. + * + * <a name="writing_equals"><h4>Writing a correct {@code equals} method</h4></a> + * <p>Follow this style to write a canonical {@code equals} method: + * <pre> + * // Use @Override to avoid accidental overloading. + * @Override public boolean equals(Object o) { + * // Return true if the objects are identical. + * // (This is just an optimization, not required for correctness.) + * if (this == o) { + * return true; + * } + * + * // Return false if the other object has the wrong type. + * // This type may be an interface depending on the interface's specification. + * if (!(o instanceof MyType)) { + * return false; + * } + * + * // Cast to the appropriate type. + * // This will succeed because of the instanceof, and lets us access private fields. + * MyType lhs = (MyType) o; + * + * // Check each field. Primitive fields, reference fields, and nullable reference + * // fields are all treated differently. + * return primitiveField == lhs.primitiveField && + * referenceField.equals(lhs.referenceField) && + * (nullableField == null ? lhs.nullableField == null + * : nullableField.equals(lhs.nullableField)); + * } + * </pre> + * <p>If you override {@code equals}, you should also override {@code hashCode}: equal + * instances must have equal hash codes. + * + * <p>See <i>Effective Java</i> item 8 for much more detail and clarification. + * + * <a name="writing_hashCode"><h4>Writing a correct {@code hashCode} method</h4></a> + * <p>Follow this style to write a canonical {@code hashCode} method: + * <pre> + * @Override public int hashCode() { + * // Start with a non-zero constant. + * int result = 17; + * + * // Include a hash for each field. + * result = 31 * result + (booleanField ? 1 : 0); + * + * result = 31 * result + byteField; + * result = 31 * result + charField; + * result = 31 * result + shortField; + * result = 31 * result + intField; + * + * result = 31 * result + (int) (longField ^ (longField >>> 32)); + * + * result = 31 * result + Float.floatToIntBits(floatField); + * + * long doubleFieldBits = Double.doubleToLongBits(doubleField); + * result = 31 * result + (int) (doubleFieldBits ^ (doubleFieldBits >>> 32)); + * + * result = 31 * result + Arrays.hashCode(arrayField); + * + * result = 31 * result + referenceField.hashCode(); + * result = 31 * result + + * (nullableReferenceField == null ? 0 + * : nullableReferenceField.hashCode()); + * + * return result; + * } + * </pre> + * + * <p>If you don't intend your type to be used as a hash key, don't simply rely on the default + * {@code hashCode} implementation, because that silently and non-obviously breaks any future + * code that does use your type as a hash key. You should throw instead: + * <pre> + * @Override public int hashCode() { + * throw new UnsupportedOperationException(); + * } + * </pre> + * + * <p>See <i>Effective Java</i> item 9 for much more detail and clarification. + * + * <a name="writing_toString"><h4>Writing a useful {@code toString} method</h4></a> + * <p>For debugging convenience, it's common to override {@code toString} in this style: + * <pre> + * @Override public String toString() { + * return getClass().getName() + "[" + + * "primitiveField=" + primitiveField + ", " + + * "referenceField=" + referenceField + ", " + + * "arrayField=" + Arrays.toString(arrayField) + "]"; + * } + * </pre> + * <p>The set of fields to include is generally the same as those that would be tested + * in your {@code equals} implementation. + * <p>See <i>Effective Java</i> item 10 for much more detail and clarification. + */ +public class Object { + /** + * Constructs a new instance of {@code Object}. + */ + public Object() { + } + + /** + * Creates and returns a copy of this {@code Object}. The default + * implementation returns a so-called "shallow" copy: It creates a new + * instance of the same class and then copies the field values (including + * object references) from this instance to the new instance. A "deep" copy, + * in contrast, would also recursively clone nested objects. A subclass that + * needs to implement this kind of cloning should call {@code super.clone()} + * to create the new instance and then create deep copies of the nested, + * mutable objects. + * + * @return a copy of this object. + * @throws CloneNotSupportedException + * if this object's class does not implement the {@code + * Cloneable} interface. + */ + protected Object clone() throws CloneNotSupportedException { + if (!(this instanceof Cloneable)) { + throw new CloneNotSupportedException("Class doesn't implement Cloneable"); + } + + return internalClone((Cloneable) this); + } + + /* + * Native helper method for cloning. + */ + private native Object internalClone(Cloneable o); + + /** + * Compares this instance with the specified object and indicates if they + * are equal. In order to be equal, {@code o} must represent the same object + * as this instance using a class-specific comparison. The general contract + * is that this comparison should be reflexive, symmetric, and transitive. + * Also, no object reference other than null is equal to null. + * + * <p>The default implementation returns {@code true} only if {@code this == + * o}. See <a href="{@docRoot}reference/java/lang/Object.html#writing_equals">Writing a correct + * {@code equals} method</a> + * if you intend implementing your own {@code equals} method. + * + * <p>The general contract for the {@code equals} and {@link + * #hashCode()} methods is that if {@code equals} returns {@code true} for + * any two objects, then {@code hashCode()} must return the same value for + * these objects. This means that subclasses of {@code Object} usually + * override either both methods or neither of them. + * + * @param o + * the object to compare this instance with. + * @return {@code true} if the specified object is equal to this {@code + * Object}; {@code false} otherwise. + * @see #hashCode + */ + public boolean equals(Object o) { + return this == o; + } + + /** + * Invoked when the garbage collector has detected that this instance is no longer reachable. + * The default implementation does nothing, but this method can be overridden to free resources. + * + * <p>Note that objects that override {@code finalize} are significantly more expensive than + * objects that don't. Finalizers may be run a long time after the object is no longer + * reachable, depending on memory pressure, so it's a bad idea to rely on them for cleanup. + * Note also that finalizers are run on a single VM-wide finalizer thread, + * so doing blocking work in a finalizer is a bad idea. A finalizer is usually only necessary + * for a class that has a native peer and needs to call a native method to destroy that peer. + * Even then, it's better to provide an explicit {@code close} method (and implement + * {@link java.io.Closeable}), and insist that callers manually dispose of instances. This + * works well for something like files, but less well for something like a {@code BigInteger} + * where typical calling code would have to deal with lots of temporaries. Unfortunately, + * code that creates lots of temporaries is the worst kind of code from the point of view of + * the single finalizer thread. + * + * <p>If you <i>must</i> use finalizers, consider at least providing your own + * {@link java.lang.ref.ReferenceQueue} and having your own thread process that queue. + * + * <p>Unlike constructors, finalizers are not automatically chained. You are responsible for + * calling {@code super.finalize()} yourself. + * + * <p>Uncaught exceptions thrown by finalizers are ignored and do not terminate the finalizer + * thread. + * + * See <i>Effective Java</i> Item 7, "Avoid finalizers" for more. + */ + @FindBugsSuppressWarnings("FI_EMPTY") + protected void finalize() throws Throwable { + } + + /** + * Returns the unique instance of {@link Class} that represents this + * object's class. Note that {@code getClass()} is a special case in that it + * actually returns {@code Class<? extends Foo>} where {@code Foo} is the + * erasure of the type of the expression {@code getClass()} was called upon. + * <p> + * As an example, the following code actually compiles, although one might + * think it shouldn't: + * <p> + * <pre>{@code + * List<Integer> l = new ArrayList<Integer>(); + * Class<? extends List> c = l.getClass();}</pre> + * + * @return this object's {@code Class} instance. + */ + public final native Class<?> getClass(); + + /** + * Returns an integer hash code for this object. By contract, any two + * objects for which {@link #equals} returns {@code true} must return + * the same hash code value. This means that subclasses of {@code Object} + * usually override both methods or neither method. + * + * <p>Note that hash values must not change over time unless information used in equals + * comparisons also changes. + * + * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_hashCode">Writing a correct + * {@code hashCode} method</a> + * if you intend implementing your own {@code hashCode} method. + * + * @return this object's hash code. + * @see #equals + */ + public native int hashCode(); + + /** + * Causes a thread which is waiting on this object's monitor (by means of + * calling one of the {@code wait()} methods) to be woken up. If more than + * one thread is waiting, one of them is chosen at the discretion of the + * VM. The chosen thread will not run immediately. The thread + * that called {@code notify()} has to release the object's monitor first. + * Also, the chosen thread still has to compete against other threads that + * try to synchronize on the same object. + * + * <p>This method can only be invoked by a thread which owns this object's + * monitor. A thread becomes owner of an object's monitor + * <ul> + * <li>by executing a synchronized method of that object;</li> + * <li>by executing the body of a {@code synchronized} statement that + * synchronizes on the object;</li> + * <li>by executing a synchronized static method if the object is of type + * {@code Class}.</li> + * </ul> + * + * @see #notifyAll + * @see #wait() + * @see #wait(long) + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final native void notify(); + + /** + * Causes all threads which are waiting on this object's monitor (by means + * of calling one of the {@code wait()} methods) to be woken up. The threads + * will not run immediately. The thread that called {@code notify()} has to + * release the object's monitor first. Also, the threads still have to + * compete against other threads that try to synchronize on the same object. + * + * <p>This method can only be invoked by a thread which owns this object's + * monitor. A thread becomes owner of an object's monitor + * <ul> + * <li>by executing a synchronized method of that object;</li> + * <li>by executing the body of a {@code synchronized} statement that + * synchronizes on the object;</li> + * <li>by executing a synchronized static method if the object is of type + * {@code Class}.</li> + * </ul> + * + * @throws IllegalMonitorStateException + * if the thread calling this method is not the owner of this + * object's monitor. + * @see #notify + * @see #wait() + * @see #wait(long) + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final native void notifyAll(); + + /** + * Returns a string containing a concise, human-readable description of this + * object. Subclasses are encouraged to override this method and provide an + * implementation that takes into account the object's type and data. The + * default implementation is equivalent to the following expression: + * <pre> + * getClass().getName() + '@' + Integer.toHexString(hashCode())</pre> + * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_toString">Writing a useful + * {@code toString} method</a> + * if you intend implementing your own {@code toString} method. + * + * @return a printable representation of this object. + */ + public String toString() { + return getClass().getName() + '@' + Integer.toHexString(hashCode()); + } + + /** + * Causes the calling thread to wait until another thread calls the {@code + * notify()} or {@code notifyAll()} method of this object. This method can + * only be invoked by a thread which owns this object's monitor; see + * {@link #notify()} on how a thread can become the owner of a monitor. + * + * <p>A waiting thread can be sent {@code interrupt()} to cause it to + * prematurely stop waiting, so {@code wait} should be called in a loop to + * check that the condition that has been waited for has been met before + * continuing. + * + * <p>While the thread waits, it gives up ownership of this object's + * monitor. When it is notified (or interrupted), it re-acquires the monitor + * before it starts running. + * + * @throws IllegalMonitorStateException + * if the thread calling this method is not the owner of this + * object's monitor. + * @throws InterruptedException if the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception + * is thrown. + * @see #notify + * @see #notifyAll + * @see #wait(long) + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final void wait() throws InterruptedException { + wait(0, 0); + } + + /** + * Causes the calling thread to wait until another thread calls the {@code + * notify()} or {@code notifyAll()} method of this object or until the + * specified timeout expires. This method can only be invoked by a thread + * which owns this object's monitor; see {@link #notify()} on how a thread + * can become the owner of a monitor. + * + * <p>A waiting thread can be sent {@code interrupt()} to cause it to + * prematurely stop waiting, so {@code wait} should be called in a loop to + * check that the condition that has been waited for has been met before + * continuing. + * + * <p>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>A timeout of zero means the calling thread should wait forever unless interrupted or + * notified. + * + * @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 the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception + * is thrown. + * @see #notify + * @see #notifyAll + * @see #wait() + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final void wait(long millis) throws InterruptedException { + wait(millis, 0); + } + + /** + * Causes the calling thread to wait until another thread calls the {@code + * notify()} or {@code notifyAll()} method of this object or until the + * specified timeout expires. This method can only be invoked by a thread + * that owns this object's monitor; see {@link #notify()} on how a thread + * can become the owner of a monitor. + * + * <p>A waiting thread can be sent {@code interrupt()} to cause it to + * prematurely stop waiting, so {@code wait} should be called in a loop to + * check that the condition that has been waited for has been met before + * continuing. + * + * <p>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>A timeout of zero means the calling thread should wait forever unless interrupted or + * notified. + * + * @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 the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception + * is thrown. + * @see #notify + * @see #notifyAll + * @see #wait() + * @see #wait(long,int) + * @see java.lang.Thread + */ + public final native void wait(long millis, int nanos) throws InterruptedException; +} diff --git a/libdvm/src/main/java/java/lang/String.java b/libdvm/src/main/java/java/lang/String.java new file mode 100644 index 0000000..10a6301 --- /dev/null +++ b/libdvm/src/main/java/java/lang/String.java @@ -0,0 +1,2075 @@ +/* + * 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; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.Charsets; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Formatter; +import java.util.Locale; +import java.util.regex.Pattern; +import libcore.util.EmptyArray; + +/** + * An immutable sequence of characters/code units ({@code char}s). A + * {@code String} is represented by array of UTF-16 values, such that + * Unicode supplementary characters (code points) are stored/encoded as + * surrogate pairs via Unicode code units ({@code char}). + * + * <a name="backing_array"><h3>Backing Arrays</h3></a> + * This class is implemented using a char[]. The length of the array may exceed + * the length of the string. For example, the string "Hello" may be backed by + * the array {@code ['H', 'e', 'l', 'l', 'o', 'W'. 'o', 'r', 'l', 'd']} with + * offset 0 and length 5. + * + * <p>Multiple strings can share the same char[] because strings are immutable. + * The {@link #substring} method <strong>always</strong> returns a string that + * shares the backing array of its source string. Generally this is an + * optimization: fewer character arrays need to be allocated, and less copying + * is necessary. But this can also lead to unwanted heap retention. Taking a + * short substring of long string means that the long shared char[] won't be + * garbage until both strings are garbage. This typically happens when parsing + * small substrings out of a large input. To avoid this where necessary, call + * {@code new String(longString.subString(...))}. The string copy constructor + * always ensures that the backing array is no larger than necessary. + * + * @see StringBuffer + * @see StringBuilder + * @see Charset + * @since 1.0 + */ +public final class String implements Serializable, Comparable<String>, CharSequence { + + private static final long serialVersionUID = -6849794470754667710L; + + private static final char REPLACEMENT_CHAR = (char) 0xfffd; + + /** + * CaseInsensitiveComparator compares Strings ignoring the case of the + * characters. + */ + private static final class CaseInsensitiveComparator implements + Comparator<String>, Serializable { + private static final long serialVersionUID = 8575799808933029326L; + + /** + * Compare the two objects to determine the relative ordering. + * + * @param o1 + * an Object to compare + * @param o2 + * an Object to compare + * @return an int < 0 if object1 is less than object2, 0 if they are + * equal, and > 0 if object1 is greater + * + * @throws ClassCastException + * if objects are not the correct type + */ + public int compare(String o1, String o2) { + return o1.compareToIgnoreCase(o2); + } + } + + /** + * A comparator ignoring the case of the characters. + */ + public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); + + private static final char[] ASCII; + static { + ASCII = new char[128]; + for (int i = 0; i < ASCII.length; ++i) { + ASCII[i] = (char) i; + } + } + + private final char[] value; + + private final int offset; + + private final int count; + + private int hashCode; + + /** + * Creates an empty string. + */ + public String() { + value = EmptyArray.CHAR; + offset = 0; + count = 0; + } + + /* + * Private constructor used for JIT optimization. + */ + @SuppressWarnings("unused") + private String(String s, char c) { + offset = 0; + value = new char[s.count + 1]; + count = s.count + 1; + System.arraycopy(s.value, s.offset, value, 0, s.count); + value[s.count] = c; + } + + /** + * Converts the byte array to a string using the system's + * {@link java.nio.charset.Charset#defaultCharset default charset}. + */ + @FindBugsSuppressWarnings("DM_DEFAULT_ENCODING") + public String(byte[] data) { + this(data, 0, data.length); + } + + /** + * Converts the byte array to a string, setting the high byte of every + * character to the specified value. + * + * @param data + * the byte array to convert to a string. + * @param high + * the high byte to use. + * @throws NullPointerException + * if {@code data == null}. + * @deprecated Use {@link #String(byte[])} or {@link #String(byte[], String)} instead. + */ + @Deprecated + public String(byte[] data, int high) { + this(data, high, 0, data.length); + } + + /** + * Converts a subsequence of the byte array to a string using the system's + * {@link java.nio.charset.Charset#defaultCharset default charset}. + * + * @throws NullPointerException + * if {@code data == null}. + * @throws IndexOutOfBoundsException + * if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}. + */ + public String(byte[] data, int offset, int byteCount) { + this(data, offset, byteCount, Charset.defaultCharset()); + } + + /** + * Converts the byte array to a string, setting the high byte of every + * character to {@code high}. + * + * @throws NullPointerException + * if {@code data == null}. + * @throws IndexOutOfBoundsException + * if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length} + * + * @deprecated Use {@link #String(byte[], int, int)} instead. + */ + @Deprecated + public String(byte[] data, int high, int offset, int byteCount) { + if ((offset | byteCount) < 0 || byteCount > data.length - offset) { + throw failedBoundsCheck(data.length, offset, byteCount); + } + this.offset = 0; + this.value = new char[byteCount]; + this.count = byteCount; + high <<= 8; + for (int i = 0; i < count; i++) { + value[i] = (char) (high + (data[offset++] & 0xff)); + } + } + + /** + * Converts the byte array to a string using the named charset. + * + * <p>The behavior when the bytes cannot be decoded by the named charset + * is unspecified. Use {@link java.nio.charset.CharsetDecoder} for more control. + * + * @throws NullPointerException + * if {@code data == null}. + * @throws IndexOutOfBoundsException + * if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}. + * @throws UnsupportedEncodingException + * if the named charset is not supported. + */ + public String(byte[] data, int offset, int byteCount, String charsetName) throws UnsupportedEncodingException { + this(data, offset, byteCount, Charset.forNameUEE(charsetName)); + } + + /** + * Converts the byte array to a string using the named charset. + * + * <p>The behavior when the bytes cannot be decoded by the named charset + * is unspecified. Use {@link java.nio.charset.CharsetDecoder} for more control. + * + * @throws NullPointerException + * if {@code data == null}. + * @throws UnsupportedEncodingException + * if {@code charsetName} is not supported. + */ + public String(byte[] data, String charsetName) throws UnsupportedEncodingException { + this(data, 0, data.length, Charset.forNameUEE(charsetName)); + } + + /** + * Converts the byte array to a string using the given charset. + * + * <p>The behavior when the bytes cannot be decoded by the given charset + * is to replace malformed input and unmappable characters with the charset's default + * replacement string. Use {@link java.nio.charset.CharsetDecoder} for more control. + * + * @throws IndexOutOfBoundsException + * if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length} + * @throws NullPointerException + * if {@code data == null} + * + * @since 1.6 + */ + public String(byte[] data, int offset, int byteCount, Charset charset) { + if ((offset | byteCount) < 0 || byteCount > data.length - offset) { + throw failedBoundsCheck(data.length, offset, byteCount); + } + + // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because 'count' and + // 'value' are final. + String canonicalCharsetName = charset.name(); + if (canonicalCharsetName.equals("UTF-8")) { + byte[] d = data; + char[] v = new char[byteCount]; + + int idx = offset; + int last = offset + byteCount; + int s = 0; +outer: + while (idx < last) { + byte b0 = d[idx++]; + if ((b0 & 0x80) == 0) { + // 0xxxxxxx + // Range: U-00000000 - U-0000007F + int val = b0 & 0xff; + v[s++] = (char) val; + } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) || + ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) { + int utfCount = 1; + if ((b0 & 0xf0) == 0xe0) utfCount = 2; + else if ((b0 & 0xf8) == 0xf0) utfCount = 3; + else if ((b0 & 0xfc) == 0xf8) utfCount = 4; + else if ((b0 & 0xfe) == 0xfc) utfCount = 5; + + // 110xxxxx (10xxxxxx)+ + // Range: U-00000080 - U-000007FF (count == 1) + // Range: U-00000800 - U-0000FFFF (count == 2) + // Range: U-00010000 - U-001FFFFF (count == 3) + // Range: U-00200000 - U-03FFFFFF (count == 4) + // Range: U-04000000 - U-7FFFFFFF (count == 5) + + if (idx + utfCount > last) { + v[s++] = REPLACEMENT_CHAR; + continue; + } + + // Extract usable bits from b0 + int val = b0 & (0x1f >> (utfCount - 1)); + for (int i = 0; i < utfCount; ++i) { + byte b = d[idx++]; + if ((b & 0xc0) != 0x80) { + v[s++] = REPLACEMENT_CHAR; + idx--; // Put the input char back + continue outer; + } + // Push new bits in from the right side + val <<= 6; + val |= b & 0x3f; + } + + // Note: Java allows overlong char + // specifications To disallow, check that val + // is greater than or equal to the minimum + // value for each count: + // + // count min value + // ----- ---------- + // 1 0x80 + // 2 0x800 + // 3 0x10000 + // 4 0x200000 + // 5 0x4000000 + + // Allow surrogate values (0xD800 - 0xDFFF) to + // be specified using 3-byte UTF values only + if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) { + v[s++] = REPLACEMENT_CHAR; + continue; + } + + // Reject chars greater than the Unicode maximum of U+10FFFF. + if (val > 0x10FFFF) { + v[s++] = REPLACEMENT_CHAR; + continue; + } + + // Encode chars from U+10000 up as surrogate pairs + if (val < 0x10000) { + v[s++] = (char) val; + } else { + int x = val & 0xffff; + int u = (val >> 16) & 0x1f; + int w = (u - 1) & 0xffff; + int hi = 0xd800 | (w << 6) | (x >> 10); + int lo = 0xdc00 | (x & 0x3ff); + v[s++] = (char) hi; + v[s++] = (char) lo; + } + } else { + // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff + v[s++] = REPLACEMENT_CHAR; + } + } + + if (s == byteCount) { + // We guessed right, so we can use our temporary array as-is. + this.offset = 0; + this.value = v; + this.count = s; + } else { + // Our temporary array was too big, so reallocate and copy. + this.offset = 0; + this.value = new char[s]; + this.count = s; + System.arraycopy(v, 0, value, 0, s); + } + } else if (canonicalCharsetName.equals("ISO-8859-1")) { + this.offset = 0; + this.value = new char[byteCount]; + this.count = byteCount; + Charsets.isoLatin1BytesToChars(data, offset, byteCount, value); + } else if (canonicalCharsetName.equals("US-ASCII")) { + this.offset = 0; + this.value = new char[byteCount]; + this.count = byteCount; + Charsets.asciiBytesToChars(data, offset, byteCount, value); + } else { + CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount)); + this.offset = 0; + this.count = cb.length(); + if (count > 0) { + // We could use cb.array() directly, but that would mean we'd have to trust + // the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later, + // which would break String's immutability guarantee. It would also tend to + // mean that we'd be wasting memory because CharsetDecoder doesn't trim the + // array. So we copy. + this.value = new char[count]; + System.arraycopy(cb.array(), 0, value, 0, count); + } else { + this.value = EmptyArray.CHAR; + } + } + } + + /** + * Converts the byte array to a String using the given charset. + * + * @throws NullPointerException if {@code data == null} + * @since 1.6 + */ + public String(byte[] data, Charset charset) { + this(data, 0, data.length, charset); + } + + /** + * Initializes this string to contain the characters in the specified + * character array. Modifying the character array after creating the string + * has no effect on the string. + * + * @throws NullPointerException if {@code data == null} + */ + public String(char[] data) { + this(data, 0, data.length); + } + + /** + * Initializes this string to contain the specified characters in the + * character array. Modifying the character array after creating the string + * has no effect on the string. + * + * @throws NullPointerException + * if {@code data == null}. + * @throws IndexOutOfBoundsException + * if {@code charCount < 0 || offset < 0 || offset + charCount > data.length} + */ + public String(char[] data, int offset, int charCount) { + if ((offset | charCount) < 0 || charCount > data.length - offset) { + throw failedBoundsCheck(data.length, offset, charCount); + } + this.offset = 0; + this.value = new char[charCount]; + this.count = charCount; + System.arraycopy(data, offset, value, 0, count); + } + + /* + * Internal version of the String(char[], int, int) constructor. + * Does not range check, null check, or copy the character array. + */ + String(int offset, int charCount, char[] chars) { + this.value = chars; + this.offset = offset; + this.count = charCount; + } + + /** + * Constructs a new string with the same sequence of characters as {@code + * toCopy}. The returned string's <a href="#backing_array">backing array</a> + * is no larger than necessary. + */ + public String(String toCopy) { + value = (toCopy.value.length == toCopy.count) + ? toCopy.value + : Arrays.copyOfRange(toCopy.value, toCopy.offset, toCopy.offset + toCopy.length()); + offset = 0; + count = value.length; + } + + /* + * Private constructor useful for JIT optimization. + */ + @SuppressWarnings( { "unused", "nls" }) + private String(String s1, String s2) { + if (s1 == null) { + s1 = "null"; + } + if (s2 == null) { + s2 = "null"; + } + count = s1.count + s2.count; + value = new char[count]; + offset = 0; + System.arraycopy(s1.value, s1.offset, value, 0, s1.count); + System.arraycopy(s2.value, s2.offset, value, s1.count, s2.count); + } + + /* + * Private constructor useful for JIT optimization. + */ + @SuppressWarnings( { "unused", "nls" }) + private String(String s1, String s2, String s3) { + if (s1 == null) { + s1 = "null"; + } + if (s2 == null) { + s2 = "null"; + } + if (s3 == null) { + s3 = "null"; + } + count = s1.count + s2.count + s3.count; + value = new char[count]; + offset = 0; + System.arraycopy(s1.value, s1.offset, value, 0, s1.count); + System.arraycopy(s2.value, s2.offset, value, s1.count, s2.count); + System.arraycopy(s3.value, s3.offset, value, s1.count + s2.count, s3.count); + } + + /** + * Creates a {@code String} from the contents of the specified + * {@code StringBuffer}. + */ + public String(StringBuffer stringBuffer) { + offset = 0; + synchronized (stringBuffer) { + value = stringBuffer.shareValue(); + count = stringBuffer.length(); + } + } + + /** + * Creates a {@code String} from the sub-array of Unicode code points. + * + * @throws NullPointerException + * if {@code codePoints == null}. + * @throws IllegalArgumentException + * if any of the elements of {@code codePoints} are not valid + * Unicode code points. + * @throws IndexOutOfBoundsException + * if {@code offset} or {@code count} are not within the bounds + * of {@code codePoints}. + * @since 1.5 + */ + public String(int[] codePoints, int offset, int count) { + if (codePoints == null) { + throw new NullPointerException("codePoints == null"); + } + if ((offset | count) < 0 || count > codePoints.length - offset) { + throw failedBoundsCheck(codePoints.length, offset, count); + } + this.offset = 0; + this.value = new char[count * 2]; + int end = offset + count; + int c = 0; + for (int i = offset; i < end; i++) { + c += Character.toChars(codePoints[i], this.value, c); + } + this.count = c; + } + + /** + * Creates a {@code String} from the contents of the specified {@code + * StringBuilder}. + * + * @throws NullPointerException + * if {@code stringBuilder == null}. + * @since 1.5 + */ + public String(StringBuilder stringBuilder) { + if (stringBuilder == null) { + throw new NullPointerException("stringBuilder == null"); + } + this.offset = 0; + this.count = stringBuilder.length(); + this.value = new char[this.count]; + stringBuilder.getChars(0, this.count, this.value, 0); + } + + /* + * Creates a {@code String} that is s1 + v1. May be used by JIT code. + */ + @SuppressWarnings("unused") + private String(String s1, int v1) { + if (s1 == null) { + s1 = "null"; + } + String s2 = String.valueOf(v1); + int len = s1.count + s2.count; + value = new char[len]; + offset = 0; + System.arraycopy(s1.value, s1.offset, value, 0, s1.count); + System.arraycopy(s2.value, s2.offset, value, s1.count, s2.count); + count = len; + } + + /** + * Returns the character at {@code index}. + * @throws IndexOutOfBoundsException if {@code index < 0} or {@code index >= length()}. + */ + public char charAt(int index) { + if (index < 0 || index >= count) { + throw indexAndLength(index); + } + return value[offset + index]; + } + + private StringIndexOutOfBoundsException indexAndLength(int index) { + throw new StringIndexOutOfBoundsException(this, index); + } + + private StringIndexOutOfBoundsException startEndAndLength(int start, int end) { + throw new StringIndexOutOfBoundsException(this, start, end - start); + } + + private StringIndexOutOfBoundsException failedBoundsCheck(int arrayLength, int offset, int count) { + throw new StringIndexOutOfBoundsException(arrayLength, offset, count); + } + + /** + * This isn't equivalent to either of ICU's u_foldCase case folds, and thus any of the Unicode + * case folds, but it's what the RI uses. + */ + private char foldCase(char ch) { + if (ch < 128) { + if ('A' <= ch && ch <= 'Z') { + return (char) (ch + ('a' - 'A')); + } + return ch; + } + return Character.toLowerCase(Character.toUpperCase(ch)); + } + + /** + * Compares the specified string to this string using the Unicode values of + * the characters. Returns 0 if the strings contain the same characters in + * the same order. Returns a negative integer if the first non-equal + * character in this string has a Unicode value which is less than the + * Unicode value of the character at the same position in the specified + * string, or if this string is a prefix of the specified string. Returns a + * positive integer if the first non-equal character in this string has a + * Unicode value which is greater than the Unicode value of the character at + * the same position in the specified string, or if the specified string is + * a prefix of this string. + * + * @param string + * the string to compare. + * @return 0 if the strings are equal, a negative integer if this string is + * before the specified string, or a positive integer if this string + * is after the specified string. + * @throws NullPointerException + * if {@code string} is {@code null}. + */ + public native int compareTo(String string); + + /** + * Compares the specified string to this string using the Unicode values of + * the characters, ignoring case differences. Returns 0 if the strings + * contain the same characters in the same order. Returns a negative integer + * if the first non-equal character in this string has a Unicode value which + * is less than the Unicode value of the character at the same position in + * the specified string, or if this string is a prefix of the specified + * string. Returns a positive integer if the first non-equal character in + * this string has a Unicode value which is greater than the Unicode value + * of the character at the same position in the specified string, or if the + * specified string is a prefix of this string. + * + * @param string + * the string to compare. + * @return 0 if the strings are equal, a negative integer if this string is + * before the specified string, or a positive integer if this string + * is after the specified string. + * @throws NullPointerException + * if {@code string} is {@code null}. + */ + public int compareToIgnoreCase(String string) { + int o1 = offset, o2 = string.offset, result; + int end = offset + (count < string.count ? count : string.count); + char c1, c2; + char[] target = string.value; + while (o1 < end) { + if ((c1 = value[o1++]) == (c2 = target[o2++])) { + continue; + } + c1 = foldCase(c1); + c2 = foldCase(c2); + if ((result = c1 - c2) != 0) { + return result; + } + } + return count - string.count; + } + + /** + * Concatenates this string and the specified string. + * + * @param string + * the string to concatenate + * @return a new string which is the concatenation of this string and the + * specified string. + */ + public String concat(String string) { + if (string.count > 0 && count > 0) { + char[] buffer = new char[count + string.count]; + System.arraycopy(value, offset, buffer, 0, count); + System.arraycopy(string.value, string.offset, buffer, count, string.count); + return new String(0, buffer.length, buffer); + } + return count == 0 ? string : this; + } + + /** + * Creates a new string containing the characters in the specified character + * array. Modifying the character array after creating the string has no + * effect on the string. + * + * @param data + * the array of characters. + * @return the new string. + * @throws NullPointerException + * if {@code data} is {@code null}. + */ + public static String copyValueOf(char[] data) { + return new String(data, 0, data.length); + } + + /** + * Creates a new string containing the specified characters in the character + * array. Modifying the character array after creating the string has no + * effect on the string. + * + * @param data + * the array of characters. + * @param start + * the starting offset in the character array. + * @param length + * the number of characters to use. + * @return the new string. + * @throws NullPointerException + * if {@code data} is {@code null}. + * @throws IndexOutOfBoundsException + * if {@code length < 0, start < 0} or {@code start + length > + * data.length}. + */ + public static String copyValueOf(char[] data, int start, int length) { + return new String(data, start, length); + } + + /** + * Compares the specified string to this string to determine if the + * specified string is a suffix. + * + * @param suffix + * the suffix to look for. + * @return {@code true} if the specified string is a suffix of this string, + * {@code false} otherwise. + * @throws NullPointerException + * if {@code suffix} is {@code null}. + */ + public boolean endsWith(String suffix) { + return regionMatches(count - suffix.count, suffix, 0, suffix.count); + } + + /** + * Compares the specified object to this string and returns true if they are + * equal. The object must be an instance of string with the same characters + * in the same order. + * + * @param other + * the object to compare. + * @return {@code true} if the specified object is equal to this string, + * {@code false} otherwise. + * @see #hashCode + */ + @Override public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other instanceof String) { + String s = (String)other; + int count = this.count; + if (s.count != count) { + return false; + } + // TODO: we want to avoid many boundchecks in the loop below + // for long Strings until we have array equality intrinsic. + // Bad benchmarks just push .equals without first getting a + // hashCode hit (unlike real world use in a Hashtable). Filter + // out these long strings here. When we get the array equality + // intrinsic then remove this use of hashCode. + if (hashCode() != s.hashCode()) { + return false; + } + char[] value1 = value; + int offset1 = offset; + char[] value2 = s.value; + int offset2 = s.offset; + for (int end = offset1 + count; offset1 < end; ) { + if (value1[offset1] != value2[offset2]) { + return false; + } + offset1++; + offset2++; + } + return true; + } else { + return false; + } + } + + /** + * Compares the specified string to this string ignoring the case of the + * characters and returns true if they are equal. + * + * @param string + * the string to compare. + * @return {@code true} if the specified string is equal to this string, + * {@code false} otherwise. + */ + @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ") + public boolean equalsIgnoreCase(String string) { + if (string == this) { + return true; + } + if (string == null || count != string.count) { + return false; + } + int o1 = offset, o2 = string.offset; + int end = offset + count; + char[] target = string.value; + while (o1 < end) { + char c1 = value[o1++]; + char c2 = target[o2++]; + if (c1 != c2 && foldCase(c1) != foldCase(c2)) { + return false; + } + } + return true; + } + + /** + * Mangles this string into a byte array by stripping the high order bits from + * each character. Use {@link #getBytes()} or {@link #getBytes(String)} instead. + * + * @param start + * the starting offset of characters to copy. + * @param end + * the ending offset of characters to copy. + * @param data + * the destination byte array. + * @param index + * the starting offset in the destination byte array. + * @throws NullPointerException + * if {@code data} is {@code null}. + * @throws IndexOutOfBoundsException + * if {@code start < 0}, {@code end > length()}, {@code index < + * 0} or {@code end - start > data.length - index}. + * @deprecated Use {@link #getBytes()} or {@link #getBytes(String)} instead. + */ + @Deprecated + public void getBytes(int start, int end, byte[] data, int index) { + // Note: last character not copied! + if (start >= 0 && start <= end && end <= count) { + end += offset; + try { + for (int i = offset + start; i < end; i++) { + data[index++] = (byte) value[i]; + } + } catch (ArrayIndexOutOfBoundsException ignored) { + throw failedBoundsCheck(data.length, index, end - start); + } + } else { + throw startEndAndLength(start, end); + } + } + + /** + * Returns a new byte array containing the characters of this string encoded using the + * system's {@link java.nio.charset.Charset#defaultCharset default charset}. + * + * <p>The behavior when this string cannot be represented in the system's default charset + * is unspecified. In practice, when the default charset is UTF-8 (as it is on Android), + * all strings can be encoded. + */ + public byte[] getBytes() { + return getBytes(Charset.defaultCharset()); + } + + /** + * Returns a new byte array containing the characters of this string encoded using the + * named charset. + * + * <p>The behavior when this string cannot be represented in the named charset + * is unspecified. Use {@link java.nio.charset.CharsetEncoder} for more control. + * + * @throws UnsupportedEncodingException if the charset is not supported + */ + public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { + return getBytes(Charset.forNameUEE(charsetName)); + } + + /** + * Returns a new byte array containing the characters of this string encoded using the + * given charset. + * + * <p>The behavior when this string cannot be represented in the given charset + * is to replace malformed input and unmappable characters with the charset's default + * replacement byte array. Use {@link java.nio.charset.CharsetEncoder} for more control. + * + * @since 1.6 + */ + public byte[] getBytes(Charset charset) { + String canonicalCharsetName = charset.name(); + if (canonicalCharsetName.equals("UTF-8")) { + return Charsets.toUtf8Bytes(value, offset, count); + } else if (canonicalCharsetName.equals("ISO-8859-1")) { + return Charsets.toIsoLatin1Bytes(value, offset, count); + } else if (canonicalCharsetName.equals("US-ASCII")) { + return Charsets.toAsciiBytes(value, offset, count); + } else if (canonicalCharsetName.equals("UTF-16BE")) { + return Charsets.toBigEndianUtf16Bytes(value, offset, count); + } else { + CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count); + ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer()); + byte[] bytes = new byte[buffer.limit()]; + buffer.get(bytes); + return bytes; + } + } + + /** + * Copies the specified characters in this string to the character array + * starting at the specified offset in the character array. + * + * @param start + * the starting offset of characters to copy. + * @param end + * the ending offset of characters to copy. + * @param buffer + * the destination character array. + * @param index + * the starting offset in the character array. + * @throws NullPointerException + * if {@code buffer} is {@code null}. + * @throws IndexOutOfBoundsException + * if {@code start < 0}, {@code end > length()}, {@code start > + * end}, {@code index < 0}, {@code end - start > buffer.length - + * index} + */ + public void getChars(int start, int end, char[] buffer, int index) { + // Note: last character not copied! + if (start >= 0 && start <= end && end <= count) { + System.arraycopy(value, start + offset, buffer, index, end - start); + } else { + // We throw StringIndexOutOfBoundsException rather than System.arraycopy's AIOOBE. + throw startEndAndLength(start, end); + } + } + + /** + * Version of getChars without bounds checks, for use by other classes + * within the java.lang package only. The caller is responsible for + * ensuring that start >= 0 && start <= end && end <= count. + */ + void _getChars(int start, int end, char[] buffer, int index) { + // NOTE last character not copied! + System.arraycopy(value, start + offset, buffer, index, end - start); + } + + @Override public int hashCode() { + int hash = hashCode; + if (hash == 0) { + if (count == 0) { + return 0; + } + final int end = count + offset; + final char[] chars = value; + for (int i = offset; i < end; ++i) { + hash = 31*hash + chars[i]; + } + hashCode = hash; + } + return hash; + } + + /** + * Searches in this string for the first index of the specified character. + * The search for the character starts at the beginning and moves towards + * the end of this string. + * + * @param c + * the character to find. + * @return the index in this string of the specified character, -1 if the + * character isn't found. + */ + public int indexOf(int c) { + // TODO: just "return indexOf(c, 0);" when the JIT can inline that deep. + if (c > 0xffff) { + return indexOfSupplementary(c, 0); + } + return fastIndexOf(c, 0); + } + + /** + * Searches in this string for the index of the specified character. The + * search for the character starts at the specified offset and moves towards + * the end of this string. + * + * @param c + * the character to find. + * @param start + * the starting offset. + * @return the index in this string of the specified character, -1 if the + * character isn't found. + */ + public int indexOf(int c, int start) { + if (c > 0xffff) { + return indexOfSupplementary(c, start); + } + return fastIndexOf(c, start); + } + + private native int fastIndexOf(int c, int start); + + private int indexOfSupplementary(int c, int start) { + if (!Character.isSupplementaryCodePoint(c)) { + return -1; + } + char[] chars = Character.toChars(c); + String needle = new String(0, chars.length, chars); + return indexOf(needle, start); + } + + /** + * Searches in this string for the first index of the specified string. The + * search for the string starts at the beginning and moves towards the end + * of this string. + * + * @param string + * the string to find. + * @return the index of the first character of the specified string in this + * string, -1 if the specified string is not a substring. + * @throws NullPointerException + * if {@code string} is {@code null}. + */ + public int indexOf(String string) { + int start = 0; + int subCount = string.count; + int _count = count; + if (subCount > 0) { + if (subCount > _count) { + return -1; + } + char[] target = string.value; + int subOffset = string.offset; + char firstChar = target[subOffset]; + int end = subOffset + subCount; + while (true) { + int i = indexOf(firstChar, start); + if (i == -1 || subCount + i > _count) { + return -1; // handles subCount > count || start >= count + } + int o1 = offset + i, o2 = subOffset; + char[] _value = value; + while (++o2 < end && _value[++o1] == target[o2]) { + // Intentionally empty + } + if (o2 == end) { + return i; + } + start = i + 1; + } + } + return start < _count ? start : _count; + } + + /** + * Searches in this string for the index of the specified string. The search + * for the string starts at the specified offset and moves towards the end + * of this string. + * + * @param subString + * the string to find. + * @param start + * the starting offset. + * @return the index of the first character of the specified string in this + * string, -1 if the specified string is not a substring. + * @throws NullPointerException + * if {@code subString} is {@code null}. + */ + public int indexOf(String subString, int start) { + if (start < 0) { + start = 0; + } + int subCount = subString.count; + int _count = count; + if (subCount > 0) { + if (subCount + start > _count) { + return -1; + } + char[] target = subString.value; + int subOffset = subString.offset; + char firstChar = target[subOffset]; + int end = subOffset + subCount; + while (true) { + int i = indexOf(firstChar, start); + if (i == -1 || subCount + i > _count) { + return -1; // handles subCount > count || start >= count + } + int o1 = offset + i, o2 = subOffset; + char[] _value = value; + while (++o2 < end && _value[++o1] == target[o2]) { + // Intentionally empty + } + if (o2 == end) { + return i; + } + start = i + 1; + } + } + return start < _count ? start : _count; + } + + /** + * Returns an interned string equal to this string. The VM maintains an internal set of + * unique strings. All string literals found in loaded classes' + * constant pools are automatically interned. Manually-interned strings are only weakly + * referenced, so calling {@code intern} won't lead to unwanted retention. + * + * <p>Interning is typically used because it guarantees that for interned strings + * {@code a} and {@code b}, {@code a.equals(b)} can be simplified to + * {@code a == b}. (This is not true of non-interned strings.) + * + * <p>Many applications find it simpler and more convenient to use an explicit + * {@link java.util.HashMap} to implement their own pools. + */ + public native String intern(); + + /** + * Returns true if the length of this string is 0. + * + * @since 1.6 + */ + public boolean isEmpty() { + return count == 0; + } + + /** + * Returns the last index of the code point {@code c}, or -1. + * The search for the character starts at the end and moves towards the + * beginning of this string. + */ + public int lastIndexOf(int c) { + if (c > 0xffff) { + return lastIndexOfSupplementary(c, Integer.MAX_VALUE); + } + int _count = count; + int _offset = offset; + char[] _value = value; + for (int i = _offset + _count - 1; i >= _offset; --i) { + if (_value[i] == c) { + return i - _offset; + } + } + return -1; + } + + /** + * Returns the last index of the code point {@code c}, or -1. + * The search for the character starts at offset {@code start} and moves towards + * the beginning of this string. + */ + public int lastIndexOf(int c, int start) { + if (c > 0xffff) { + return lastIndexOfSupplementary(c, start); + } + int _count = count; + int _offset = offset; + char[] _value = value; + if (start >= 0) { + if (start >= _count) { + start = _count - 1; + } + for (int i = _offset + start; i >= _offset; --i) { + if (_value[i] == c) { + return i - _offset; + } + } + } + return -1; + } + + private int lastIndexOfSupplementary(int c, int start) { + if (!Character.isSupplementaryCodePoint(c)) { + return -1; + } + char[] chars = Character.toChars(c); + String needle = new String(0, chars.length, chars); + return lastIndexOf(needle, start); + } + + /** + * Searches in this string for the last index of the specified string. The + * search for the string starts at the end and moves towards the beginning + * of this string. + * + * @param string + * the string to find. + * @return the index of the first character of the specified string in this + * string, -1 if the specified string is not a substring. + * @throws NullPointerException + * if {@code string} is {@code null}. + */ + public int lastIndexOf(String string) { + // Use count instead of count - 1 so lastIndexOf("") returns count + return lastIndexOf(string, count); + } + + /** + * Searches in this string for the index of the specified string. The search + * for the string starts at the specified offset and moves towards the + * beginning of this string. + * + * @param subString + * the string to find. + * @param start + * the starting offset. + * @return the index of the first character of the specified string in this + * string , -1 if the specified string is not a substring. + * @throws NullPointerException + * if {@code subString} is {@code null}. + */ + public int lastIndexOf(String subString, int start) { + int subCount = subString.count; + if (subCount <= count && start >= 0) { + if (subCount > 0) { + if (start > count - subCount) { + start = count - subCount; + } + // count and subCount are both >= 1 + char[] target = subString.value; + int subOffset = subString.offset; + char firstChar = target[subOffset]; + int end = subOffset + subCount; + while (true) { + int i = lastIndexOf(firstChar, start); + if (i == -1) { + return -1; + } + int o1 = offset + i, o2 = subOffset; + while (++o2 < end && value[++o1] == target[o2]) { + // Intentionally empty + } + if (o2 == end) { + return i; + } + start = i - 1; + } + } + return start < count ? start : count; + } + return -1; + } + + /** + * Returns the number of characters in this string. + */ + public int length() { + return count; + } + + /** + * Compares the specified string to this string and compares the specified + * range of characters to determine if they are the same. + * + * @param thisStart + * the starting offset in this string. + * @param string + * the string to compare. + * @param start + * the starting offset in the specified string. + * @param length + * the number of characters to compare. + * @return {@code true} if the ranges of characters are equal, {@code false} + * otherwise + * @throws NullPointerException + * if {@code string} is {@code null}. + */ + public boolean regionMatches(int thisStart, String string, int start, int length) { + if (string == null) { + throw new NullPointerException("string == null"); + } + if (start < 0 || string.count - start < length) { + return false; + } + if (thisStart < 0 || count - thisStart < length) { + return false; + } + if (length <= 0) { + return true; + } + int o1 = offset + thisStart, o2 = string.offset + start; + char[] value1 = value; + char[] value2 = string.value; + for (int i = 0; i < length; ++i) { + if (value1[o1 + i] != value2[o2 + i]) { + return false; + } + } + return true; + } + + /** + * Compares the specified string to this string and compares the specified + * range of characters to determine if they are the same. When ignoreCase is + * true, the case of the characters is ignored during the comparison. + * + * @param ignoreCase + * specifies if case should be ignored. + * @param thisStart + * the starting offset in this string. + * @param string + * the string to compare. + * @param start + * the starting offset in the specified string. + * @param length + * the number of characters to compare. + * @return {@code true} if the ranges of characters are equal, {@code false} + * otherwise. + * @throws NullPointerException + * if {@code string} is {@code null}. + */ + public boolean regionMatches(boolean ignoreCase, int thisStart, String string, int start, int length) { + if (!ignoreCase) { + return regionMatches(thisStart, string, start, length); + } + if (string == null) { + throw new NullPointerException("string == null"); + } + if (thisStart < 0 || length > count - thisStart) { + return false; + } + if (start < 0 || length > string.count - start) { + return false; + } + thisStart += offset; + start += string.offset; + int end = thisStart + length; + char[] target = string.value; + while (thisStart < end) { + char c1 = value[thisStart++]; + char c2 = target[start++]; + if (c1 != c2 && foldCase(c1) != foldCase(c2)) { + return false; + } + } + return true; + } + + /** + * Copies this string replacing occurrences of the specified character with + * another character. + * + * @param oldChar + * the character to replace. + * @param newChar + * the replacement character. + * @return a new string with occurrences of oldChar replaced by newChar. + */ + public String replace(char oldChar, char newChar) { + char[] buffer = value; + int _offset = offset; + int _count = count; + + int idx = _offset; + int last = _offset + _count; + boolean copied = false; + while (idx < last) { + if (buffer[idx] == oldChar) { + if (!copied) { + char[] newBuffer = new char[_count]; + System.arraycopy(buffer, _offset, newBuffer, 0, _count); + buffer = newBuffer; + idx -= _offset; + last -= _offset; + copied = true; + } + buffer[idx] = newChar; + } + idx++; + } + + return copied ? new String(0, count, buffer) : this; + } + + /** + * Copies this string replacing occurrences of the specified target sequence + * with another sequence. The string is processed from the beginning to the + * end. + * + * @param target + * the sequence to replace. + * @param replacement + * the replacement sequence. + * @return the resulting string. + * @throws NullPointerException + * if {@code target} or {@code replacement} is {@code null}. + */ + public String replace(CharSequence target, CharSequence replacement) { + if (target == null) { + throw new NullPointerException("target == null"); + } + if (replacement == null) { + throw new NullPointerException("replacement == null"); + } + + String targetString = target.toString(); + int matchStart = indexOf(targetString, 0); + if (matchStart == -1) { + // If there's nothing to replace, return the original string untouched. + return this; + } + + String replacementString = replacement.toString(); + + // The empty target matches at the start and end and between each character. + int targetLength = targetString.length(); + if (targetLength == 0) { + // The result contains the original 'count' characters, a copy of the + // replacement string before every one of those characters, and a final + // copy of the replacement string at the end. + int resultLength = count + (count + 1) * replacementString.length(); + StringBuilder result = new StringBuilder(resultLength); + result.append(replacementString); + int end = offset + count; + for (int i = offset; i != end; ++i) { + result.append(value[i]); + result.append(replacementString); + } + return result.toString(); + } + + StringBuilder result = new StringBuilder(count); + int searchStart = 0; + do { + // Copy characters before the match... + result.append(value, offset + searchStart, matchStart - searchStart); + // Insert the replacement... + result.append(replacementString); + // And skip over the match... + searchStart = matchStart + targetLength; + } while ((matchStart = indexOf(targetString, searchStart)) != -1); + // Copy any trailing chars... + result.append(value, offset + searchStart, count - searchStart); + return result.toString(); + } + + /** + * Compares the specified string to this string to determine if the + * specified string is a prefix. + * + * @param prefix + * the string to look for. + * @return {@code true} if the specified string is a prefix of this string, + * {@code false} otherwise + * @throws NullPointerException + * if {@code prefix} is {@code null}. + */ + public boolean startsWith(String prefix) { + return startsWith(prefix, 0); + } + + /** + * Compares the specified string to this string, starting at the specified + * offset, to determine if the specified string is a prefix. + * + * @param prefix + * the string to look for. + * @param start + * the starting offset. + * @return {@code true} if the specified string occurs in this string at the + * specified offset, {@code false} otherwise. + * @throws NullPointerException + * if {@code prefix} is {@code null}. + */ + public boolean startsWith(String prefix, int start) { + return regionMatches(start, prefix, 0, prefix.count); + } + + /** + * Returns a string containing a suffix of this string. The returned string + * shares this string's <a href="#backing_array">backing array</a>. + * + * @param start + * the offset of the first character. + * @return a new string containing the characters from start to the end of + * the string. + * @throws IndexOutOfBoundsException + * if {@code start < 0} or {@code start > length()}. + */ + public String substring(int start) { + if (start == 0) { + return this; + } + if (start >= 0 && start <= count) { + return new String(offset + start, count - start, value); + } + throw indexAndLength(start); + } + + /** + * Returns a string containing a subsequence of characters from this string. + * The returned string shares this string's <a href="#backing_array">backing + * array</a>. + * + * @param start + * the offset of the first character. + * @param end + * the offset one past the last character. + * @return a new string containing the characters from start to end - 1 + * @throws IndexOutOfBoundsException + * if {@code start < 0}, {@code start > end} or {@code end > + * length()}. + */ + public String substring(int start, int end) { + if (start == 0 && end == count) { + return this; + } + // NOTE last character not copied! + // Fast range check. + if (start >= 0 && start <= end && end <= count) { + return new String(offset + start, end - start, value); + } + throw startEndAndLength(start, end); + } + + /** + * Returns a new {@code char} array containing a copy of the characters in this string. + * This is expensive and rarely useful. If you just want to iterate over the characters in + * the string, use {@link #charAt} instead. + */ + public char[] toCharArray() { + char[] buffer = new char[count]; + System.arraycopy(value, offset, buffer, 0, count); + return buffer; + } + + /** + * Converts this string to lower case, using the rules of the user's default locale. + * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>". + * + * @return a new lower case string, or {@code this} if it's already all lower case. + */ + public String toLowerCase() { + return CaseMapper.toLowerCase(Locale.getDefault(), this, value, offset, count); + } + + /** + * Converts this string to lower case, using the rules of {@code locale}. + * + * <p>Most case mappings are unaffected by the language of a {@code Locale}. Exceptions include + * dotted and dotless I in Azeri and Turkish locales, and dotted and dotless I and J in + * Lithuanian locales. On the other hand, it isn't necessary to provide a Greek locale to get + * correct case mapping of Greek characters: any locale will do. + * + * <p>See <a href="http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt">http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt</a> + * for full details of context- and language-specific special cases. + * + * @return a new lower case string, or {@code this} if it's already all lower case. + */ + public String toLowerCase(Locale locale) { + return CaseMapper.toLowerCase(locale, this, value, offset, count); + } + + /** + * Returns this string. + */ + @Override + public String toString() { + return this; + } + + /** + * Converts this this string to upper case, using the rules of the user's default locale. + * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>". + * + * @return a new upper case string, or {@code this} if it's already all upper case. + */ + public String toUpperCase() { + return CaseMapper.toUpperCase(Locale.getDefault(), this, value, offset, count); + } + + /** + * Converts this this string to upper case, using the rules of {@code locale}. + * + * <p>Most case mappings are unaffected by the language of a {@code Locale}. Exceptions include + * dotted and dotless I in Azeri and Turkish locales, and dotted and dotless I and J in + * Lithuanian locales. On the other hand, it isn't necessary to provide a Greek locale to get + * correct case mapping of Greek characters: any locale will do. + * + * <p>See <a href="http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt">http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt</a> + * for full details of context- and language-specific special cases. + * + * @return a new upper case string, or {@code this} if it's already all upper case. + */ + public String toUpperCase(Locale locale) { + return CaseMapper.toUpperCase(locale, this, value, offset, count); + } + + /** + * Copies this string removing white space characters from the beginning and + * end of the string. + * + * @return a new string with characters <code><= \\u0020</code> removed from + * the beginning and the end. + */ + public String trim() { + int start = offset, last = offset + count - 1; + int end = last; + while ((start <= end) && (value[start] <= ' ')) { + start++; + } + while ((end >= start) && (value[end] <= ' ')) { + end--; + } + if (start == offset && end == last) { + return this; + } + return new String(start, end - start + 1, value); + } + + /** + * Creates a new string containing the characters in the specified character + * array. Modifying the character array after creating the string has no + * effect on the string. + * + * @param data + * the array of characters. + * @return the new string. + * @throws NullPointerException + * if {@code data} is {@code null}. + */ + public static String valueOf(char[] data) { + return new String(data, 0, data.length); + } + + /** + * Creates a new string containing the specified characters in the character + * array. Modifying the character array after creating the string has no + * effect on the string. + * + * @param data + * the array of characters. + * @param start + * the starting offset in the character array. + * @param length + * the number of characters to use. + * @return the new string. + * @throws IndexOutOfBoundsException + * if {@code length < 0}, {@code start < 0} or {@code start + + * length > data.length} + * @throws NullPointerException + * if {@code data} is {@code null}. + */ + public static String valueOf(char[] data, int start, int length) { + return new String(data, start, length); + } + + /** + * Converts the specified character to its string representation. + * + * @param value + * the character. + * @return the character converted to a string. + */ + public static String valueOf(char value) { + String s; + if (value < 128) { + s = new String(value, 1, ASCII); + } else { + s = new String(0, 1, new char[] { value }); + } + s.hashCode = value; + return s; + } + + /** + * Converts the specified double to its string representation. + * + * @param value + * the double. + * @return the double converted to a string. + */ + public static String valueOf(double value) { + return Double.toString(value); + } + + /** + * Converts the specified float to its string representation. + * + * @param value + * the float. + * @return the float converted to a string. + */ + public static String valueOf(float value) { + return Float.toString(value); + } + + /** + * Converts the specified integer to its string representation. + * + * @param value + * the integer. + * @return the integer converted to a string. + */ + public static String valueOf(int value) { + return Integer.toString(value); + } + + /** + * Converts the specified long to its string representation. + * + * @param value + * the long. + * @return the long converted to a string. + */ + public static String valueOf(long value) { + return Long.toString(value); + } + + /** + * Converts the specified object to its string representation. If the object + * is null return the string {@code "null"}, otherwise use {@code + * toString()} to get the string representation. + * + * @param value + * the object. + * @return the object converted to a string, or the string {@code "null"}. + */ + public static String valueOf(Object value) { + return value != null ? value.toString() : "null"; + } + + /** + * Converts the specified boolean to its string representation. When the + * boolean is {@code true} return {@code "true"}, otherwise return {@code + * "false"}. + * + * @param value + * the boolean. + * @return the boolean converted to a string. + */ + public static String valueOf(boolean value) { + return value ? "true" : "false"; + } + + /** + * Returns whether the characters in the StringBuffer {@code strbuf} are the + * same as those in this string. + * + * @param strbuf + * the StringBuffer to compare this string to. + * @return {@code true} if the characters in {@code strbuf} are identical to + * those in this string. If they are not, {@code false} will be + * returned. + * @throws NullPointerException + * if {@code strbuf} is {@code null}. + * @since 1.4 + */ + public boolean contentEquals(StringBuffer strbuf) { + synchronized (strbuf) { + int size = strbuf.length(); + if (count != size) { + return false; + } + return regionMatches(0, new String(0, size, strbuf.getValue()), 0, + size); + } + } + + /** + * Compares a {@code CharSequence} to this {@code String} to determine if + * their contents are equal. + * + * @param cs + * the character sequence to compare to. + * @return {@code true} if equal, otherwise {@code false} + * @since 1.5 + */ + public boolean contentEquals(CharSequence cs) { + if (cs == null) { + throw new NullPointerException("cs == null"); + } + + int len = cs.length(); + + if (len != count) { + return false; + } + + if (len == 0 && count == 0) { + return true; // since both are empty strings + } + + return regionMatches(0, cs.toString(), 0, len); + } + + /** + * Tests whether this string matches the given {@code regularExpression}. This method returns + * true only if the regular expression matches the <i>entire</i> input string. A common mistake is + * to assume that this method behaves like {@link #contains}; if you want to match anywhere + * within the input string, you need to add {@code .*} to the beginning and end of your + * regular expression. See {@link Pattern#matches}. + * + * <p>If the same regular expression is to be used for multiple operations, it may be more + * efficient to reuse a compiled {@code Pattern}. + * + * @throws PatternSyntaxException + * if the syntax of the supplied regular expression is not + * valid. + * @throws NullPointerException if {@code regularExpression == null} + * @since 1.4 + */ + public boolean matches(String regularExpression) { + return Pattern.matches(regularExpression, this); + } + + /** + * Replaces all matches for {@code regularExpression} within this string with the given + * {@code replacement}. + * See {@link Pattern} for regular expression syntax. + * + * <p>If the same regular expression is to be used for multiple operations, it may be more + * efficient to reuse a compiled {@code Pattern}. + * + * @throws PatternSyntaxException + * if the syntax of the supplied regular expression is not + * valid. + * @throws NullPointerException if {@code regularExpression == null} + * @see Pattern + * @since 1.4 + */ + public String replaceAll(String regularExpression, String replacement) { + return Pattern.compile(regularExpression).matcher(this).replaceAll(replacement); + } + + /** + * Replaces the first match for {@code regularExpression} within this string with the given + * {@code replacement}. + * See {@link Pattern} for regular expression syntax. + * + * <p>If the same regular expression is to be used for multiple operations, it may be more + * efficient to reuse a compiled {@code Pattern}. + * + * @throws PatternSyntaxException + * if the syntax of the supplied regular expression is not + * valid. + * @throws NullPointerException if {@code regularExpression == null} + * @see Pattern + * @since 1.4 + */ + public String replaceFirst(String regularExpression, String replacement) { + return Pattern.compile(regularExpression).matcher(this).replaceFirst(replacement); + } + + /** + * Splits this string using the supplied {@code regularExpression}. + * Equivalent to {@code split(regularExpression, 0)}. + * See {@link Pattern#split(CharSequence, int)} for an explanation of {@code limit}. + * See {@link Pattern} for regular expression syntax. + * + * <p>If the same regular expression is to be used for multiple operations, it may be more + * efficient to reuse a compiled {@code Pattern}. + * + * @throws NullPointerException if {@code regularExpression == null} + * @throws PatternSyntaxException + * if the syntax of the supplied regular expression is not + * valid. + * @see Pattern + * @since 1.4 + */ + public String[] split(String regularExpression) { + return split(regularExpression, 0); + } + + /** + * Splits this string using the supplied {@code regularExpression}. + * See {@link Pattern#split(CharSequence, int)} for an explanation of {@code limit}. + * See {@link Pattern} for regular expression syntax. + * + * <p>If the same regular expression is to be used for multiple operations, it may be more + * efficient to reuse a compiled {@code Pattern}. + * + * @throws NullPointerException if {@code regularExpression == null} + * @throws PatternSyntaxException + * if the syntax of the supplied regular expression is not + * valid. + * @since 1.4 + */ + public String[] split(String regularExpression, int limit) { + String[] result = java.util.regex.Splitter.fastSplit(regularExpression, this, limit); + return result != null ? result : Pattern.compile(regularExpression).split(this, limit); + } + + /** + * Has the same result as the substring function, but is present so that + * string may implement the CharSequence interface. + * + * @param start + * the offset the first character. + * @param end + * the offset of one past the last character to include. + * @return the subsequence requested. + * @throws IndexOutOfBoundsException + * if {@code start < 0}, {@code end < 0}, {@code start > end} or + * {@code end > length()}. + * @see java.lang.CharSequence#subSequence(int, int) + * @since 1.4 + */ + public CharSequence subSequence(int start, int end) { + return substring(start, end); + } + + /** + * Returns the Unicode code point at the given {@code index}. + * + * @throws IndexOutOfBoundsException if {@code index < 0 || index >= length()} + * @see Character#codePointAt(char[], int, int) + * @since 1.5 + */ + public int codePointAt(int index) { + if (index < 0 || index >= count) { + throw indexAndLength(index); + } + return Character.codePointAt(value, offset + index, offset + count); + } + + /** + * Returns the Unicode code point that precedes the given {@code index}. + * + * @throws IndexOutOfBoundsException if {@code index < 1 || index > length()} + * @see Character#codePointBefore(char[], int, int) + * @since 1.5 + */ + public int codePointBefore(int index) { + if (index < 1 || index > count) { + throw indexAndLength(index); + } + return Character.codePointBefore(value, offset + index, offset); + } + + /** + * Calculates the number of Unicode code points between {@code start} + * and {@code end}. + * + * @param start + * the inclusive beginning index of the subsequence. + * @param end + * the exclusive end index of the subsequence. + * @return the number of Unicode code points in the subsequence. + * @throws IndexOutOfBoundsException + * if {@code start < 0 || end > length() || start > end} + * @see Character#codePointCount(CharSequence, int, int) + * @since 1.5 + */ + public int codePointCount(int start, int end) { + if (start < 0 || end > count || start > end) { + throw startEndAndLength(start, end); + } + return Character.codePointCount(value, offset + start, end - start); + } + + /** + * Determines if this {@code String} contains the sequence of characters in + * the {@code CharSequence} passed. + * + * @param cs + * the character sequence to search for. + * @return {@code true} if the sequence of characters are contained in this + * string, otherwise {@code false}. + * @since 1.5 + */ + public boolean contains(CharSequence cs) { + if (cs == null) { + throw new NullPointerException("cs == null"); + } + return indexOf(cs.toString()) >= 0; + } + + /** + * Returns the index within this object that is offset from {@code index} by + * {@code codePointOffset} code points. + * + * @param index + * the index within this object to calculate the offset from. + * @param codePointOffset + * the number of code points to count. + * @return the index within this object that is the offset. + * @throws IndexOutOfBoundsException + * if {@code index} is negative or greater than {@code length()} + * or if there aren't enough code points before or after {@code + * index} to match {@code codePointOffset}. + * @since 1.5 + */ + public int offsetByCodePoints(int index, int codePointOffset) { + int s = index + offset; + int r = Character.offsetByCodePoints(value, offset, count, s, codePointOffset); + return r - offset; + } + + /** + * Returns a localized formatted string, using the supplied format and arguments, + * using the user's default locale. + * + * <p>If you're formatting a string other than for human + * consumption, you should use the {@code format(Locale, String, Object...)} + * overload and supply {@code Locale.US}. See + * "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>". + * + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + * @return the formatted string. + * @throws NullPointerException if {@code format == null} + * @throws java.util.IllegalFormatException + * if the format is invalid. + * @since 1.5 + */ + public static String format(String format, Object... args) { + return format(Locale.getDefault(), format, args); + } + + /** + * Returns a formatted string, using the supplied format and arguments, + * localized to the given locale. + * + * @param locale + * the locale to apply; {@code null} value means no localization. + * @param format the format string (see {@link java.util.Formatter#format}) + * @param args + * the list of arguments passed to the formatter. If there are + * more arguments than required by {@code format}, + * additional arguments are ignored. + * @return the formatted string. + * @throws NullPointerException if {@code format == null} + * @throws java.util.IllegalFormatException + * if the format is invalid. + * @since 1.5 + */ + public static String format(Locale locale, String format, Object... args) { + if (format == null) { + throw new NullPointerException("format == null"); + } + int bufferSize = format.length() + (args == null ? 0 : args.length * 10); + Formatter f = new Formatter(new StringBuilder(bufferSize), locale); + return f.format(format, args).toString(); + } + + /* + * An implementation of a String.indexOf that is supposed to perform + * substantially better than the default algorithm if the "needle" (the + * subString being searched for) is a constant string. + * + * For example, a JIT, upon encountering a call to String.indexOf(String), + * where the needle is a constant string, may compute the values cache, md2 + * and lastChar, and change the call to the following method. + */ + @FindBugsSuppressWarnings("UPM_UNCALLED_PRIVATE_METHOD") + @SuppressWarnings("unused") + private static int indexOf(String haystackString, String needleString, + int cache, int md2, char lastChar) { + char[] haystack = haystackString.value; + int haystackOffset = haystackString.offset; + int haystackLength = haystackString.count; + char[] needle = needleString.value; + int needleOffset = needleString.offset; + int needleLength = needleString.count; + int needleLengthMinus1 = needleLength - 1; + int haystackEnd = haystackOffset + haystackLength; + outer_loop: for (int i = haystackOffset + needleLengthMinus1; i < haystackEnd;) { + if (lastChar == haystack[i]) { + for (int j = 0; j < needleLengthMinus1; ++j) { + if (needle[j + needleOffset] != haystack[i + j + - needleLengthMinus1]) { + int skip = 1; + if ((cache & (1 << haystack[i])) == 0) { + skip += j; + } + i += Math.max(md2, skip); + continue outer_loop; + } + } + return i - needleLengthMinus1 - haystackOffset; + } + + if ((cache & (1 << haystack[i])) == 0) { + i += needleLengthMinus1; + } + i++; + } + return -1; + } +} diff --git a/libdvm/src/main/java/java/lang/Thread.java b/libdvm/src/main/java/java/lang/Thread.java new file mode 100644 index 0000000..a40dde1 --- /dev/null +++ b/libdvm/src/main/java/java/lang/Thread.java @@ -0,0 +1,1288 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +import dalvik.system.VMStack; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import libcore.util.EmptyArray; + +/** + * A {@code Thread} is a concurrent unit of execution. It has its own call stack + * for methods being invoked, their arguments and local variables. Each application + * has at least one thread running when it is started, the main thread, in the main + * {@link ThreadGroup}. The runtime keeps its own threads in the system thread + * group. + * + * <p>There are two ways to execute code in a new thread. + * You can either subclass {@code Thread} and overriding its {@link #run()} method, + * or construct a new {@code Thread} and pass a {@link Runnable} to the constructor. + * In either case, the {@link #start()} method must be called to actually execute + * the new {@code Thread}. + * + * <p>Each {@code Thread} has an integer priority that affect how the thread is + * scheduled by the OS. A new thread inherits the priority of its parent. + * A thread's priority can be set using the {@link #setPriority(int)} method. + */ +public class Thread implements Runnable { + private static final int NANOS_PER_MILLI = 1000000; + + /** Park states */ + private static class ParkState { + /** park state indicating unparked */ + private static final int UNPARKED = 1; + + /** park state indicating preemptively unparked */ + private static final int PREEMPTIVELY_UNPARKED = 2; + + /** park state indicating parked */ + private static final int PARKED = 3; + } + + /** + * A representation of a thread's state. A given thread may only be in one + * state at a time. + */ + public enum State { + /** + * The thread has been created, but has never been started. + */ + NEW, + /** + * The thread may be run. + */ + RUNNABLE, + /** + * The thread is blocked and waiting for a lock. + */ + BLOCKED, + /** + * The thread is waiting. + */ + WAITING, + /** + * The thread is waiting for a specified amount of time. + */ + TIMED_WAITING, + /** + * The thread has been terminated. + */ + TERMINATED + } + + /** + * The maximum priority value allowed for a thread. + * This corresponds to (but does not have the same value as) + * {@code android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY}. + */ + public static final int MAX_PRIORITY = 10; + + /** + * The minimum priority value allowed for a thread. + * This corresponds to (but does not have the same value as) + * {@code android.os.Process.THREAD_PRIORITY_LOWEST}. + */ + public static final int MIN_PRIORITY = 1; + + /** + * The normal (default) priority value assigned to the main thread. + * This corresponds to (but does not have the same value as) + * {@code android.os.Process.THREAD_PRIORITY_DEFAULT}. + + */ + public static final int NORM_PRIORITY = 5; + + /* some of these are accessed directly by the VM; do not rename them */ + volatile VMThread vmThread; + volatile ThreadGroup group; + volatile boolean daemon; + volatile String name; + volatile int priority; + volatile long stackSize; + Runnable target; + private static int count = 0; + + /** + * Holds the thread's ID. We simply count upwards, so + * each Thread has a unique ID. + */ + private long id; + + /** + * Normal thread local values. + */ + ThreadLocal.Values localValues; + + /** + * Inheritable thread local values. + */ + ThreadLocal.Values inheritableValues; + + /** Callbacks to run on interruption. */ + private final List<Runnable> interruptActions = new ArrayList<Runnable>(); + + /** + * Holds the class loader for this Thread, in case there is one. + */ + private ClassLoader contextClassLoader; + + /** + * Holds the handler for uncaught exceptions in this Thread, + * in case there is one. + */ + private UncaughtExceptionHandler uncaughtHandler; + + /** + * Holds the default handler for uncaught exceptions, in case there is one. + */ + private static UncaughtExceptionHandler defaultUncaughtHandler; + + /** + * Reflects whether this Thread has already been started. A Thread + * can only be started once (no recycling). Also, we need it to deduce + * the proper Thread status. + */ + boolean hasBeenStarted = false; + + /** the park state of the thread */ + private int parkState = ParkState.UNPARKED; + + /** The synchronization object responsible for this thread parking. */ + private Object parkBlocker; + + /** + * Constructs a new {@code Thread} with no {@code Runnable} object and a + * newly generated name. The new {@code Thread} will belong to the same + * {@code ThreadGroup} as the {@code Thread} calling this constructor. + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread() { + create(null, null, null, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object and a + * newly generated name. The new {@code Thread} will belong to the same + * {@code ThreadGroup} as the {@code Thread} calling this constructor. + * + * @param runnable + * a {@code Runnable} whose method <code>run</code> will be + * executed by the new {@code Thread} + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(Runnable runnable) { + create(null, runnable, null, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object and name + * provided. The new {@code Thread} will belong to the same {@code + * ThreadGroup} as the {@code Thread} calling this constructor. + * + * @param runnable + * a {@code Runnable} whose method <code>run</code> will be + * executed by the new {@code Thread} + * @param threadName + * the name for the {@code Thread} being created + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(Runnable runnable, String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(null, runnable, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with no {@code Runnable} object and the + * name provided. The new {@code Thread} will belong to the same {@code + * ThreadGroup} as the {@code Thread} calling this constructor. + * + * @param threadName + * the name for the {@code Thread} being created + * + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + * + */ + public Thread(String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(null, null, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object and a + * newly generated name. The new {@code Thread} will belong to the {@code + * ThreadGroup} passed as parameter. + * + * @param group + * {@code ThreadGroup} to which the new {@code Thread} will + * belong + * @param runnable + * a {@code Runnable} whose method <code>run</code> will be + * executed by the new {@code Thread} + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, Runnable runnable) { + create(group, runnable, null, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object, the given + * name and belonging to the {@code ThreadGroup} passed as parameter. + * + * @param group + * ThreadGroup to which the new {@code Thread} will belong + * @param runnable + * a {@code Runnable} whose method <code>run</code> will be + * executed by the new {@code Thread} + * @param threadName + * the name for the {@code Thread} being created + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, Runnable runnable, String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(group, runnable, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with no {@code Runnable} object, the + * given name and belonging to the {@code ThreadGroup} passed as parameter. + * + * @param group + * {@code ThreadGroup} to which the new {@code Thread} will belong + * @param threadName + * the name for the {@code Thread} being created + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + create(group, null, threadName, 0); + } + + /** + * Constructs a new {@code Thread} with a {@code Runnable} object, the given + * name and belonging to the {@code ThreadGroup} passed as parameter. + * + * @param group + * {@code ThreadGroup} to which the new {@code Thread} will + * belong + * @param runnable + * a {@code Runnable} whose method <code>run</code> will be + * executed by the new {@code Thread} + * @param threadName + * the name for the {@code Thread} being created + * @param stackSize + * a stack size for the new {@code Thread}. This has a highly + * platform-dependent interpretation. It may even be ignored + * completely. + * @throws IllegalThreadStateException + * if <code>group.destroy()</code> has already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + create(group, runnable, threadName, stackSize); + } + + /** + * Package-scope method invoked by Dalvik VM to create "internal" + * threads or attach threads created externally. + * + * Don't call Thread.currentThread(), since there may not be such + * a thing (e.g. for Main). + */ + Thread(ThreadGroup group, String name, int priority, boolean daemon) { + synchronized (Thread.class) { + id = ++Thread.count; + } + + if (name == null) { + this.name = "Thread-" + id; + } else { + this.name = name; + } + + if (group == null) { + throw new InternalError("group not specified"); + } + + this.group = group; + + this.target = null; + this.stackSize = 0; + this.priority = priority; + this.daemon = daemon; + + /* add ourselves to our ThreadGroup of choice */ + this.group.addThread(this); + } + + /** + * Initializes a new, existing Thread object with a runnable object, + * the given name and belonging to the ThreadGroup passed as parameter. + * This is the method that the several public constructors delegate their + * work to. + * + * @param group ThreadGroup to which the new Thread will belong + * @param runnable a java.lang.Runnable whose method <code>run</code> will + * be executed by the new Thread + * @param threadName Name for the Thread being created + * @param stackSize Platform dependent stack size + * @throws IllegalThreadStateException if <code>group.destroy()</code> has + * already been done + * @see java.lang.ThreadGroup + * @see java.lang.Runnable + */ + private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { + Thread currentThread = Thread.currentThread(); + if (group == null) { + group = currentThread.getThreadGroup(); + } + + if (group.isDestroyed()) { + throw new IllegalThreadStateException("Group already destroyed"); + } + + this.group = group; + + synchronized (Thread.class) { + id = ++Thread.count; + } + + if (threadName == null) { + this.name = "Thread-" + id; + } else { + this.name = threadName; + } + + this.target = runnable; + this.stackSize = stackSize; + + this.priority = currentThread.getPriority(); + + this.contextClassLoader = currentThread.contextClassLoader; + + // Transfer over InheritableThreadLocals. + if (currentThread.inheritableValues != null) { + inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues); + } + + // add ourselves to our ThreadGroup of choice + this.group.addThread(this); + } + + /** + * Returns the number of active {@code Thread}s in the running {@code + * Thread}'s group and its subgroups. + * + * @return the number of {@code Thread}s + */ + public static int activeCount() { + return currentThread().getThreadGroup().activeCount(); + } + + /** + * Does nothing. + */ + public final void checkAccess() { + } + + /** + * Returns the number of stack frames in this thread. + * + * @return Number of stack frames + * @deprecated The results of this call were never well defined. To make + * things worse, it would depend on whether the Thread was + * suspended or not, and suspend was deprecated too. + */ + @Deprecated + public int countStackFrames() { + return getStackTrace().length; + } + + /** + * Returns the Thread of the caller, that is, the current Thread. + * + * @return the current Thread. + */ + public static Thread currentThread() { + return VMThread.currentThread(); + } + + /** + * Throws {@code UnsupportedOperationException}. + * @deprecated Not implemented. + */ + @Deprecated + public void destroy() { + throw new UnsupportedOperationException(); + } + + /** + * Prints to the standard error stream a text representation of the current + * stack for this Thread. + * + * @see Throwable#printStackTrace() + */ + public static void dumpStack() { + new Throwable("stack dump").printStackTrace(); + } + + /** + * Copies an array with all Threads which are in the same ThreadGroup as the + * receiver - and subgroups - into the array <code>threads</code> passed as + * parameter. If the array passed as parameter is too small no exception is + * thrown - the extra elements are simply not copied. + * + * @param threads + * array into which the Threads will be copied + * @return How many Threads were copied over + */ + public static int enumerate(Thread[] threads) { + Thread thread = Thread.currentThread(); + return thread.getThreadGroup().enumerate(threads); + } + + /** + * Returns a map of all the currently live threads to their stack traces. + */ + public static Map<Thread, StackTraceElement[]> getAllStackTraces() { + Map<Thread, StackTraceElement[]> map = new HashMap<Thread, StackTraceElement[]>(); + + // Find out how many live threads we have. Allocate a bit more + // space than needed, in case new ones are just being created. + int count = ThreadGroup.mSystem.activeCount(); + Thread[] threads = new Thread[count + count / 2]; + + // Enumerate the threads and collect the stacktraces. + count = ThreadGroup.mSystem.enumerate(threads); + for (int i = 0; i < count; i++) { + map.put(threads[i], threads[i].getStackTrace()); + } + + return map; + } + + /** + * Returns the context ClassLoader for this Thread. + * + * @return ClassLoader The context ClassLoader + * @see java.lang.ClassLoader + * @see #getContextClassLoader() + */ + public ClassLoader getContextClassLoader() { + return contextClassLoader; + } + + /** + * Returns the default exception handler that's executed when uncaught + * exception terminates a thread. + * + * @return an {@link UncaughtExceptionHandler} or <code>null</code> if + * none exists. + */ + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return defaultUncaughtHandler; + } + + /** + * Returns the thread's identifier. The ID is a positive <code>long</code> + * generated on thread creation, is unique to the thread, and doesn't change + * during the lifetime of the thread; the ID may be reused after the thread + * has been terminated. + * + * @return the thread's ID. + */ + public long getId() { + return id; + } + + /** + * Returns the name of the Thread. + */ + public final String getName() { + return name; + } + + /** + * Returns the priority of the Thread. + */ + public final int getPriority() { + return priority; + } + + /** + * Returns an array of {@link StackTraceElement} representing the current thread's stack. + */ + public StackTraceElement[] getStackTrace() { + StackTraceElement ste[] = VMStack.getThreadStackTrace(this); + return ste != null ? ste : EmptyArray.STACK_TRACE_ELEMENT; + } + + /** + * Returns the current state of the Thread. This method is useful for + * monitoring purposes. + * + * @return a {@link State} value. + */ + public State getState() { + // TODO This is ugly and should be implemented better. + VMThread vmt = this.vmThread; + + // Make sure we have a valid reference to an object. If native code + // deletes the reference we won't run into a null reference later. + VMThread thread = vmThread; + if (thread != null) { + // If the Thread Object became invalid or was not yet started, + // getStatus() will return -1. + int state = thread.getStatus(); + if(state != -1) { + return VMThread.STATE_MAP[state]; + } + } + return hasBeenStarted ? Thread.State.TERMINATED : Thread.State.NEW; + } + + /** + * Returns the ThreadGroup to which this Thread belongs. + * + * @return the Thread's ThreadGroup + */ + public final ThreadGroup getThreadGroup() { + // TODO This should actually be done at native termination. + if (getState() == Thread.State.TERMINATED) { + return null; + } else { + return group; + } + } + + /** + * Returns the thread's uncaught exception handler. If not explicitly set, + * then the ThreadGroup's handler is returned. If the thread is terminated, + * then <code>null</code> is returned. + * + * @return an {@link UncaughtExceptionHandler} instance or {@code null}. + */ + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + if (uncaughtHandler != null) + return uncaughtHandler; + else + return group; // ThreadGroup is instance of UEH + } + + /** + * Posts an interrupt request to this {@code Thread}. The behavior depends on + * the state of this {@code Thread}: + * <ul> + * <li> + * {@code Thread}s blocked in one of {@code Object}'s {@code wait()} methods + * or one of {@code Thread}'s {@code join()} or {@code sleep()} methods will + * be woken up, their interrupt status will be cleared, and they receive an + * {@link InterruptedException}. + * <li> + * {@code Thread}s blocked in an I/O operation of an + * {@link java.nio.channels.InterruptibleChannel} will have their interrupt + * status set and receive an + * {@link java.nio.channels.ClosedByInterruptException}. Also, the channel + * will be closed. + * <li> + * {@code Thread}s blocked in a {@link java.nio.channels.Selector} will have + * their interrupt status set and return immediately. They don't receive an + * exception in this case. + * <ul> + * + * @see Thread#interrupted + * @see Thread#isInterrupted + */ + public void interrupt() { + // Interrupt this thread before running actions so that other + // threads that observe the interrupt as a result of an action + // will see that this thread is in the interrupted state. + VMThread vmt = this.vmThread; + if (vmt != null) { + vmt.interrupt(); + } + + synchronized (interruptActions) { + for (int i = interruptActions.size() - 1; i >= 0; i--) { + interruptActions.get(i).run(); + } + } + } + + /** + * Returns a <code>boolean</code> indicating whether the current Thread ( + * <code>currentThread()</code>) has a pending interrupt request (<code> + * true</code>) or not (<code>false</code>). It also has the side-effect of + * clearing the flag. + * + * @return a <code>boolean</code> indicating the interrupt status + * @see Thread#currentThread + * @see Thread#interrupt + * @see Thread#isInterrupted + */ + public static boolean interrupted() { + return VMThread.interrupted(); + } + + /** + * Returns <code>true</code> if the receiver has already been started and + * still runs code (hasn't died yet). Returns <code>false</code> either if + * the receiver hasn't been started yet or if it has already started and run + * to completion and died. + * + * @return a <code>boolean</code> indicating the liveness of the Thread + * @see Thread#start + */ + public final boolean isAlive() { + return (vmThread != null); + } + + /** + * Tests whether this is a daemon thread. + * A daemon thread only runs as long as there are non-daemon threads running. + * When the last non-daemon thread ends, the runtime will exit. This is not + * normally relevant to applications with a UI. + */ + public final boolean isDaemon() { + return daemon; + } + + /** + * Returns a <code>boolean</code> indicating whether the receiver has a + * pending interrupt request (<code>true</code>) or not ( + * <code>false</code>) + * + * @return a <code>boolean</code> indicating the interrupt status + * @see Thread#interrupt + * @see Thread#interrupted + */ + public boolean isInterrupted() { + VMThread vmt = this.vmThread; + if (vmt != null) { + return vmt.isInterrupted(); + } + + return false; + } + + /** + * Blocks the current Thread (<code>Thread.currentThread()</code>) until + * the receiver finishes its execution and dies. + * + * @throws InterruptedException if the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception is + * thrown. + * @see Object#notifyAll + * @see java.lang.ThreadDeath + */ + public final void join() throws InterruptedException { + VMThread t = vmThread; + if (t == null) { + return; + } + + synchronized (t) { + while (isAlive()) { + t.wait(); + } + } + } + + /** + * Blocks the current Thread (<code>Thread.currentThread()</code>) until + * the receiver finishes its execution and dies or the specified timeout + * expires, whatever happens first. + * + * <p>A timeout of zero means the calling thread should wait forever unless interrupted. + * + * @param millis The maximum time to wait (in milliseconds). + * @throws InterruptedException if the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception is + * thrown. + * @see Object#notifyAll + * @see java.lang.ThreadDeath + */ + public final void join(long millis) throws InterruptedException { + join(millis, 0); + } + + /** + * Blocks the current Thread (<code>Thread.currentThread()</code>) until + * the receiver finishes its execution and dies or the specified timeout + * expires, whatever happens first. + * + * <p>A timeout of zero means the calling thread should wait forever unless interrupted. + * + * @param millis The maximum time to wait (in milliseconds). + * @param nanos Extra nanosecond precision + * @throws InterruptedException if the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception is + * thrown. + * @see Object#notifyAll + * @see java.lang.ThreadDeath + */ + public final void join(long millis, int nanos) throws InterruptedException { + if (millis < 0 || nanos < 0 || nanos >= NANOS_PER_MILLI) { + throw new IllegalArgumentException("bad timeout: millis=" + millis + ",nanos=" + nanos); + } + + // avoid overflow: if total > 292,277 years, just wait forever + boolean overflow = millis >= (Long.MAX_VALUE - nanos) / NANOS_PER_MILLI; + boolean forever = (millis | nanos) == 0; + if (forever | overflow) { + join(); + return; + } + + VMThread t = vmThread; + if (t == null) { + return; + } + + synchronized (t) { + if (!isAlive()) { + return; + } + + // guaranteed not to overflow + long nanosToWait = millis * NANOS_PER_MILLI + nanos; + + // wait until this thread completes or the timeout has elapsed + long start = System.nanoTime(); + while (true) { + t.wait(millis, nanos); + if (!isAlive()) { + break; + } + long nanosElapsed = System.nanoTime() - start; + long nanosRemaining = nanosToWait - nanosElapsed; + if (nanosRemaining <= 0) { + break; + } + millis = nanosRemaining / NANOS_PER_MILLI; + nanos = (int) (nanosRemaining - millis * NANOS_PER_MILLI); + } + } + } + + /** + * Throws {@code UnsupportedOperationException}. + * @deprecated Only useful in conjunction with deprecated method {@link Thread#suspend}. + */ + @Deprecated + public final void resume() { + throw new UnsupportedOperationException(); + } + + /** + * Calls the <code>run()</code> method of the Runnable object the receiver + * holds. If no Runnable is set, does nothing. + * + * @see Thread#start + */ + public void run() { + if (target != null) { + target.run(); + } + } + + /** + * Set the context ClassLoader for the receiver. + * + * @param cl The context ClassLoader + * @see #getContextClassLoader() + */ + public void setContextClassLoader(ClassLoader cl) { + contextClassLoader = cl; + } + + /** + * Marks this thread as a daemon thread. + * A daemon thread only runs as long as there are non-daemon threads running. + * When the last non-daemon thread ends, the runtime will exit. This is not + * normally relevant to applications with a UI. + * @throws IllegalThreadStateException - if this thread has already started. + */ + public final void setDaemon(boolean isDaemon) { + checkNotStarted(); + if (vmThread == null) { + daemon = isDaemon; + } + } + + private void checkNotStarted() { + if (hasBeenStarted) { + throw new IllegalThreadStateException("Thread already started"); + } + } + + /** + * Sets the default uncaught exception handler. This handler is invoked in + * case any Thread dies due to an unhandled exception. + * + * @param handler + * The handler to set or null. + */ + public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) { + Thread.defaultUncaughtHandler = handler; + } + + /** + * Adds a runnable to be invoked upon interruption. If this thread has + * already been interrupted, the runnable will be invoked immediately. The + * action should be idempotent as it may be invoked multiple times for a + * single interruption. + * + * <p>Each call to this method must be matched with a corresponding call to + * {@link #popInterruptAction$}. + * + * @hide used by NIO + */ + public final void pushInterruptAction$(Runnable interruptAction) { + synchronized (interruptActions) { + interruptActions.add(interruptAction); + } + + if (interruptAction != null && isInterrupted()) { + interruptAction.run(); + } + } + + /** + * Removes {@code interruptAction} so it is not invoked upon interruption. + * + * @param interruptAction the pushed action, used to check that the call + * stack is correctly nested. + * + * @hide used by NIO + */ + public final void popInterruptAction$(Runnable interruptAction) { + synchronized (interruptActions) { + Runnable removed = interruptActions.remove(interruptActions.size() - 1); + if (interruptAction != removed) { + throw new IllegalArgumentException( + "Expected " + interruptAction + " but was " + removed); + } + } + } + + /** + * Sets the name of the Thread. + * + * @param threadName the new name for the Thread + * @see Thread#getName + */ + public final void setName(String threadName) { + if (threadName == null) { + throw new NullPointerException("threadName == null"); + } + + name = threadName; + VMThread vmt = this.vmThread; + if (vmt != null) { + /* notify the VM that the thread name has changed */ + vmt.nameChanged(threadName); + } + } + + /** + * Sets the priority of this thread. If the requested priority is greater than the + * parent thread group's {@link java.lang.ThreadGroup#getMaxPriority}, the group's maximum + * priority will be used instead. + * + * @throws IllegalArgumentException - if the new priority is greater than {@link #MAX_PRIORITY} + * or less than {@link #MIN_PRIORITY} + */ + public final void setPriority(int priority) { + if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) { + throw new IllegalArgumentException("Priority out of range: " + priority); + } + + if (priority > group.getMaxPriority()) { + priority = group.getMaxPriority(); + } + + this.priority = priority; + + VMThread vmt = this.vmThread; + if (vmt != null) { + vmt.setPriority(priority); + } + } + + /** + * <p> + * Sets the uncaught exception handler. This handler is invoked in case this + * Thread dies due to an unhandled exception. + * </p> + * + * @param handler + * The handler to set or <code>null</code>. + */ + public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { + uncaughtHandler = handler; + } + + /** + * Causes the thread which sent this message to sleep for the given interval + * of time (given in milliseconds). The precision is not guaranteed - the + * Thread may sleep more or less than requested. + * + * @param time + * The time to sleep in milliseconds. + * @throws InterruptedException if the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception + * is thrown. + * @see Thread#interrupt() + */ + public static void sleep(long time) throws InterruptedException { + Thread.sleep(time, 0); + } + + /** + * Causes the thread which sent this message to sleep for the given interval + * of time (given in milliseconds and nanoseconds). The precision is not + * guaranteed - the Thread may sleep more or less than requested. + * + * @param millis + * The time to sleep in milliseconds. + * @param nanos + * Extra nanosecond precision + * @throws InterruptedException if the current thread has been interrupted. + * The interrupted status of the current thread will be cleared before the exception + * is thrown. + * @see Thread#interrupt() + */ + public static void sleep(long millis, int nanos) throws InterruptedException { + VMThread.sleep(millis, nanos); + } + + /** + * Starts the new Thread of execution. The <code>run()</code> method of + * the receiver will be called by the receiver Thread itself (and not the + * Thread calling <code>start()</code>). + * + * @throws IllegalThreadStateException - if this thread has already started. + * @see Thread#run + */ + public synchronized void start() { + checkNotStarted(); + + hasBeenStarted = true; + + VMThread.create(this, stackSize); + } + + /** + * Requests the receiver Thread to stop and throw ThreadDeath. The Thread is + * resumed if it was suspended and awakened if it was sleeping, so that it + * can proceed to throw ThreadDeath. + * + * @deprecated Stopping a thread in this manner is unsafe and can + * leave your application and the VM in an unpredictable state. + */ + @Deprecated + public final void stop() { + stop(new ThreadDeath()); + } + + /** + * Throws {@code UnsupportedOperationException}. + * @deprecated Stopping a thread in this manner is unsafe and can + * leave your application and the VM in an unpredictable state. + */ + @Deprecated + public final synchronized void stop(Throwable throwable) { + throw new UnsupportedOperationException(); + } + + /** + * Throws {@code UnsupportedOperationException}. + * @deprecated May cause deadlocks. + */ + @Deprecated + public final void suspend() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a string containing a concise, human-readable description of the + * Thread. It includes the Thread's name, priority, and group name. + * + * @return a printable representation for the receiver. + */ + @Override + public String toString() { + return "Thread[" + name + "," + priority + "," + group.getName() + "]"; + } + + /** + * Causes the calling Thread to yield execution time to another Thread that + * is ready to run. The actual scheduling is implementation-dependent. + */ + public static void yield() { + VMThread.yield(); + } + + /** + * Indicates whether the current Thread has a monitor lock on the specified + * object. + * + * @param object the object to test for the monitor lock + * @return true if the current thread has a monitor lock on the specified + * object; false otherwise + */ + public static boolean holdsLock(Object object) { + return currentThread().vmThread.holdsLock(object); + } + + /** + * Implemented by objects that want to handle cases where a thread is being + * terminated by an uncaught exception. Upon such termination, the handler + * is notified of the terminating thread and causal exception. If there is + * no explicit handler set then the thread's group is the default handler. + */ + public static interface UncaughtExceptionHandler { + /** + * The thread is being terminated by an uncaught exception. Further + * exceptions thrown in this method are prevent the remainder of the + * method from executing, but are otherwise ignored. + * + * @param thread the thread that has an uncaught exception + * @param ex the exception that was thrown + */ + void uncaughtException(Thread thread, Throwable ex); + } + + /** + * Unparks this thread. This unblocks the thread it if it was + * previously parked, or indicates that the thread is "preemptively + * unparked" if it wasn't already parked. The latter means that the + * next time the thread is told to park, it will merely clear its + * latent park bit and carry on without blocking. + * + * <p>See {@link java.util.concurrent.locks.LockSupport} for more + * in-depth information of the behavior of this method.</p> + * + * @hide for Unsafe + */ + public void unpark() { + VMThread vmt = vmThread; + + if (vmt == null) { + /* + * vmThread is null before the thread is start()ed. In + * this case, we just go ahead and set the state to + * PREEMPTIVELY_UNPARKED. Since this happens before the + * thread is started, we don't have to worry about + * synchronizing with it. + */ + parkState = ParkState.PREEMPTIVELY_UNPARKED; + return; + } + + synchronized (vmt) { + switch (parkState) { + case ParkState.PREEMPTIVELY_UNPARKED: { + /* + * Nothing to do in this case: By definition, a + * preemptively unparked thread is to remain in + * the preemptively unparked state if it is told + * to unpark. + */ + break; + } + case ParkState.UNPARKED: { + parkState = ParkState.PREEMPTIVELY_UNPARKED; + break; + } + default /*parked*/: { + parkState = ParkState.UNPARKED; + vmt.notifyAll(); + break; + } + } + } + } + + /** + * Parks the current thread for a particular number of nanoseconds, or + * indefinitely. If not indefinitely, this method unparks the thread + * after the given number of nanoseconds if no other thread unparks it + * first. If the thread has been "preemptively unparked," this method + * cancels that unparking and returns immediately. This method may + * also return spuriously (that is, without the thread being told to + * unpark and without the indicated amount of time elapsing). + * + * <p>See {@link java.util.concurrent.locks.LockSupport} for more + * in-depth information of the behavior of this method.</p> + * + * <p>This method must only be called when <code>this</code> is the current + * thread. + * + * @param nanos number of nanoseconds to park for or <code>0</code> + * to park indefinitely + * @throws IllegalArgumentException thrown if <code>nanos < 0</code> + * + * @hide for Unsafe + */ + public void parkFor(long nanos) { + VMThread vmt = vmThread; + + if (vmt == null) { + // Running threads should always have an associated vmThread. + throw new AssertionError(); + } + + synchronized (vmt) { + switch (parkState) { + case ParkState.PREEMPTIVELY_UNPARKED: { + parkState = ParkState.UNPARKED; + break; + } + case ParkState.UNPARKED: { + long millis = nanos / NANOS_PER_MILLI; + nanos %= NANOS_PER_MILLI; + + parkState = ParkState.PARKED; + try { + vmt.wait(millis, (int) nanos); + } catch (InterruptedException ex) { + interrupt(); + } finally { + /* + * Note: If parkState manages to become + * PREEMPTIVELY_UNPARKED before hitting this + * code, it should left in that state. + */ + if (parkState == ParkState.PARKED) { + parkState = ParkState.UNPARKED; + } + } + break; + } + default /*parked*/: { + throw new AssertionError( + "shouldn't happen: attempt to repark"); + } + } + } + } + + /** + * Parks the current thread until the specified system time. This + * method attempts to unpark the current thread immediately after + * <code>System.currentTimeMillis()</code> reaches the specified + * value, if no other thread unparks it first. If the thread has + * been "preemptively unparked," this method cancels that + * unparking and returns immediately. This method may also return + * spuriously (that is, without the thread being told to unpark + * and without the indicated amount of time elapsing). + * + * <p>See {@link java.util.concurrent.locks.LockSupport} for more + * in-depth information of the behavior of this method.</p> + * + * <p>This method must only be called when <code>this</code> is the + * current thread. + * + * @param time the time after which the thread should be unparked, + * in absolute milliseconds-since-the-epoch + * + * @hide for Unsafe + */ + public void parkUntil(long time) { + VMThread vmt = vmThread; + + if (vmt == null) { + // Running threads should always have an associated vmThread. + throw new AssertionError(); + } + + synchronized (vmt) { + /* + * Note: This conflates the two time bases of "wall clock" + * time and "monotonic uptime" time. However, given that + * the underlying system can only wait on monotonic time, + * it is unclear if there is any way to avoid the + * conflation. The downside here is that if, having + * calculated the delay, the wall clock gets moved ahead, + * this method may not return until well after the wall + * clock has reached the originally designated time. The + * reverse problem (the wall clock being turned back) + * isn't a big deal, since this method is allowed to + * spuriously return for any reason, and this situation + * can safely be construed as just such a spurious return. + */ + long delayMillis = time - System.currentTimeMillis(); + + if (delayMillis <= 0) { + parkState = ParkState.UNPARKED; + } else { + parkFor(delayMillis * NANOS_PER_MILLI); + } + } + } +} diff --git a/libdvm/src/main/java/java/lang/ThreadGroup.java b/libdvm/src/main/java/java/lang/ThreadGroup.java new file mode 100644 index 0000000..7fc6b72 --- /dev/null +++ b/libdvm/src/main/java/java/lang/ThreadGroup.java @@ -0,0 +1,726 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import libcore.util.CollectionUtils; + +/** + * {@code ThreadGroup} is a means of organizing threads into a hierarchical structure. + * This class is obsolete. See <i>Effective Java</i> Item 73, "Avoid thread groups" for details. + * @see Thread + */ +public class ThreadGroup implements Thread.UncaughtExceptionHandler { + + // Name of this ThreadGroup + // VM needs this field name for debugging. + private String name; + + // Maximum priority for Threads inside this ThreadGroup + private int maxPriority = Thread.MAX_PRIORITY; + + // The ThreadGroup to which this ThreadGroup belongs + // VM needs this field name for debugging. + final ThreadGroup parent; + + /** + * Weak references to the threads in this group. + * Access is guarded by synchronizing on this field. + */ + private final List<WeakReference<Thread>> threadRefs = new ArrayList<WeakReference<Thread>>(5); + + /** + * View of the threads. + * Access is guarded by synchronizing on threadRefs. + */ + private final Iterable<Thread> threads = CollectionUtils.dereferenceIterable(threadRefs, true); + + /** + * Thread groups. Access is guarded by synchronizing on this field. + */ + private final List<ThreadGroup> groups = new ArrayList<ThreadGroup>(3); + + // Whether this ThreadGroup is a daemon ThreadGroup or not + private boolean isDaemon; + + // Whether this ThreadGroup has already been destroyed or not + private boolean isDestroyed; + + /* the VM uses these directly; do not rename */ + static final ThreadGroup mSystem = new ThreadGroup(); + static final ThreadGroup mMain = new ThreadGroup(mSystem, "main"); + + /** + * Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup} + * will be child of the {@code ThreadGroup} to which the calling thread belongs. + * + * @param name the name + * @see Thread#currentThread + */ + public ThreadGroup(String name) { + this(Thread.currentThread().getThreadGroup(), name); + } + + /** + * Constructs a new {@code ThreadGroup} with the given name, as a child of the + * given {@code ThreadGroup}. + * + * @param parent the parent + * @param name the name + * @throws NullPointerException if {@code parent == null} + * @throws IllegalThreadStateException if {@code parent} has been + * destroyed already + */ + public ThreadGroup(ThreadGroup parent, String name) { + if (parent == null) { + throw new NullPointerException("parent == null"); + } + this.name = name; + this.parent = parent; + if (parent != null) { + parent.add(this); + this.setMaxPriority(parent.getMaxPriority()); + if (parent.isDaemon()) { + this.setDaemon(true); + } + } + } + + /** + * Initialize the special "system" ThreadGroup. Was "main" in Harmony, + * but we have an additional group above that in Android. + */ + private ThreadGroup() { + this.name = "system"; + this.parent = null; + } + + /** + * Returns the number of running {@code Thread}s which are children of this thread group, + * directly or indirectly. + * + * @return the number of children + */ + public int activeCount() { + int count = 0; + synchronized (threadRefs) { + for (Thread thread : threads) { + if (thread.isAlive()) { + count++; + } + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + count += group.activeCount(); + } + } + return count; + } + + /** + * Returns the number of {@code ThreadGroup}s which are children of this group, + * directly or indirectly. + * + * @return the number of children + */ + public int activeGroupCount() { + int count = 0; + synchronized (groups) { + for (ThreadGroup group : groups) { + // One for this group & the subgroups + count += 1 + group.activeGroupCount(); + } + } + return count; + } + + /** + * Adds a {@code ThreadGroup} to this thread group. + * + * @param g ThreadGroup to add + * @throws IllegalThreadStateException if this group has been destroyed already + */ + private void add(ThreadGroup g) throws IllegalThreadStateException { + synchronized (groups) { + if (isDestroyed) { + throw new IllegalThreadStateException(); + } + groups.add(g); + } + } + + /** + * Does nothing. The definition of this method depends on the deprecated + * method {@link #suspend()}. The exact behavior of this call was never + * specified. + * + * @param b Used to control low memory implicit suspension + * @return {@code true} (always) + * + * @deprecated Required deprecated method {@link #suspend()}. + */ + @Deprecated + public boolean allowThreadSuspension(boolean b) { + // Does not apply to this VM, no-op + return true; + } + + /** + * Does nothing. + */ + public final void checkAccess() { + } + + /** + * Destroys this thread group and recursively all its subgroups. It is only legal + * to destroy a {@code ThreadGroup} that has no threads in it. Any daemon + * {@code ThreadGroup} is destroyed automatically when it becomes empty (no threads + * or thread groups in it). + * + * @throws IllegalThreadStateException if this thread group or any of its + * subgroups has been destroyed already or if it still contains + * threads. + */ + public final void destroy() { + synchronized (threadRefs) { + synchronized (groups) { + if (isDestroyed) { + throw new IllegalThreadStateException( + "Thread group was already destroyed: " + + (this.name != null ? this.name : "n/a")); + } + if (threads.iterator().hasNext()) { + throw new IllegalThreadStateException( + "Thread group still contains threads: " + + (this.name != null ? this.name : "n/a")); + } + // Call recursively for subgroups + while (!groups.isEmpty()) { + // We always get the first element - remember, when the + // child dies it removes itself from our collection. See + // below. + groups.get(0).destroy(); + } + + if (parent != null) { + parent.remove(this); + } + + // Now that the ThreadGroup is really destroyed it can be tagged as so + this.isDestroyed = true; + } + } + } + + /* + * Auxiliary method that destroys this thread group and recursively all its + * subgroups if this is a daemon ThreadGroup. + * + * @see #destroy + * @see #setDaemon + * @see #isDaemon + */ + private void destroyIfEmptyDaemon() { + // Has to be non-destroyed daemon to make sense + synchronized (threadRefs) { + if (isDaemon && !isDestroyed && !threads.iterator().hasNext()) { + synchronized (groups) { + if (groups.isEmpty()) { + destroy(); + } + } + } + } + } + + /** + * Iterates over all active threads in this group (and its sub-groups) and + * stores the threads in the given array. Returns when the array is full or + * no more threads remain, whichever happens first. + * + * <p>Note that this method will silently ignore any threads that don't fit in the + * supplied array. + * + * @param threads the array into which the {@code Thread}s will be copied + * @return the number of {@code Thread}s that were copied + */ + public int enumerate(Thread[] threads) { + return enumerate(threads, true); + } + + /** + * Iterates over all active threads in this group (and, optionally, its + * sub-groups) and stores the threads in the given array. Returns when the + * array is full or no more threads remain, whichever happens first. + * + * <p>Note that this method will silently ignore any threads that don't fit in the + * supplied array. + * + * @param threads the array into which the {@code Thread}s will be copied + * @param recurse indicates whether {@code Thread}s in subgroups should be + * recursively copied as well + * @return the number of {@code Thread}s that were copied + */ + public int enumerate(Thread[] threads, boolean recurse) { + return enumerateGeneric(threads, recurse, 0, true); + } + + /** + * Iterates over all thread groups in this group (and its sub-groups) and + * and stores the groups in the given array. Returns when the array is full + * or no more groups remain, whichever happens first. + * + * <p>Note that this method will silently ignore any thread groups that don't fit in the + * supplied array. + * + * @param groups the array into which the {@code ThreadGroup}s will be copied + * @return the number of {@code ThreadGroup}s that were copied + */ + public int enumerate(ThreadGroup[] groups) { + return enumerate(groups, true); + } + + /** + * Iterates over all thread groups in this group (and, optionally, its + * sub-groups) and stores the groups in the given array. Returns when + * the array is full or no more groups remain, whichever happens first. + * + * <p>Note that this method will silently ignore any thread groups that don't fit in the + * supplied array. + * + * @param groups the array into which the {@code ThreadGroup}s will be copied + * @param recurse indicates whether {@code ThreadGroup}s in subgroups should be + * recursively copied as well or not + * @return the number of {@code ThreadGroup}s that were copied + */ + public int enumerate(ThreadGroup[] groups, boolean recurse) { + return enumerateGeneric(groups, recurse, 0, false); + } + + /** + * Copies into <param>enumeration</param> starting at + * <param>enumerationIndex</param> all Threads or ThreadGroups in the + * receiver. If <param>recurse</param> is true, recursively enumerate the + * elements in subgroups. + * + * If the array passed as parameter is too small no exception is thrown - + * the extra elements are simply not copied. + * + * @param enumeration array into which the elements will be copied + * @param recurse Indicates whether subgroups should be enumerated or not + * @param enumerationIndex Indicates in which position of the enumeration + * array we are + * @param enumeratingThreads Indicates whether we are enumerating Threads or + * ThreadGroups + * @return How many elements were enumerated/copied over + */ + private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, + boolean enumeratingThreads) { + if (enumeratingThreads) { + synchronized (threadRefs) { + // walk the references directly so we can iterate in reverse order + for (int i = threadRefs.size() - 1; i >= 0; --i) { + Thread thread = threadRefs.get(i).get(); + if (thread != null && thread.isAlive()) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumeration[enumerationIndex++] = thread; + } + } + } + } else { + synchronized (groups) { + for (int i = groups.size() - 1; i >= 0; --i) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumeration[enumerationIndex++] = groups.get(i); + } + } + } + + if (recurse) { + synchronized (groups) { + for (ThreadGroup group : groups) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumerationIndex = group.enumerateGeneric(enumeration, recurse, + enumerationIndex, enumeratingThreads); + } + } + } + return enumerationIndex; + } + + /** + * Returns the maximum allowed priority for a {@code Thread} in this thread group. + * + * @return the maximum priority + * + * @see #setMaxPriority + */ + public final int getMaxPriority() { + return maxPriority; + } + + /** + * Returns the name of this thread group. + * + * @return the group's name + */ + public final String getName() { + return name; + } + + /** + * Returns this thread group's parent {@code ThreadGroup}. It can be null if this + * is the the root ThreadGroup. + * + * @return the parent + */ + public final ThreadGroup getParent() { + return parent; + } + + /** + * Interrupts every {@code Thread} in this group and recursively in all its + * subgroups. + * + * @see Thread#interrupt + */ + public final void interrupt() { + synchronized (threadRefs) { + for (Thread thread : threads) { + thread.interrupt(); + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + group.interrupt(); + } + } + } + + /** + * Checks whether this thread group is a daemon {@code ThreadGroup}. + * + * @return true if this thread group is a daemon {@code ThreadGroup} + * + * @see #setDaemon + * @see #destroy + */ + public final boolean isDaemon() { + return isDaemon; + } + + /** + * Checks whether this thread group has already been destroyed. + * + * @return true if this thread group has already been destroyed + * @see #destroy + */ + public synchronized boolean isDestroyed() { + return isDestroyed; + } + + /** + * Outputs to {@code System.out} a text representation of the + * hierarchy of {@code Thread}s and {@code ThreadGroup}s in this thread group (and recursively). + * Proper indentation is used to show the nesting of groups inside groups + * and threads inside groups. + */ + public void list() { + // We start in a fresh line + System.out.println(); + list(0); + } + + /* + * Outputs to {@code System.out}a text representation of the + * hierarchy of Threads and ThreadGroups in this thread group (and recursively). + * The indentation will be four spaces per level of nesting. + * + * @param levels How many levels of nesting, so that proper indentation can + * be output. + */ + private void list(int levels) { + indent(levels); + System.out.println(this.toString()); + + ++levels; + synchronized (threadRefs) { + for (Thread thread : threads) { + indent(levels); + System.out.println(thread); + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + group.list(levels); + } + } + } + + private void indent(int levels) { + for (int i = 0; i < levels; i++) { + System.out.print(" "); // 4 spaces for each level + } + } + + /** + * Checks whether this thread group is a direct or indirect parent group of a + * given {@code ThreadGroup}. + * + * @param g the potential child {@code ThreadGroup} + * @return true if this thread group is parent of {@code g} + */ + public final boolean parentOf(ThreadGroup g) { + while (g != null) { + if (this == g) { + return true; + } + g = g.parent; + } + return false; + } + + /** + * Removes an immediate subgroup. + * + * @param g ThreadGroup to remove + * + * @see #add(Thread) + * @see #add(ThreadGroup) + */ + private void remove(ThreadGroup g) { + synchronized (groups) { + for (Iterator<ThreadGroup> i = groups.iterator(); i.hasNext(); ) { + ThreadGroup threadGroup = i.next(); + if (threadGroup.equals(g)) { + i.remove(); + break; + } + } + } + destroyIfEmptyDaemon(); + } + + /** + * Resumes every thread in this group and recursively in all its + * subgroups. + * + * @see Thread#resume + * @see #suspend + * + * @deprecated Requires deprecated method {@link Thread#resume()}. + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void resume() { + synchronized (threadRefs) { + for (Thread thread : threads) { + thread.resume(); + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + group.resume(); + } + } + } + + /** + * Sets whether this is a daemon {@code ThreadGroup} or not. Daemon + * thread groups are automatically destroyed when they become empty. + * + * @param isDaemon the new value + * @see #isDaemon + * @see #destroy + */ + public final void setDaemon(boolean isDaemon) { + this.isDaemon = isDaemon; + } + + /** + * Configures the maximum allowed priority for a {@code Thread} in this group and + * recursively in all its subgroups. + * + * <p>A caller can never increase the maximum priority of a thread group. + * Such an attempt will not result in an exception, it will + * simply leave the thread group with its current maximum priority. + * + * @param newMax the new maximum priority to be set + * + * @throws IllegalArgumentException if the new priority is greater than + * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY + * + * @see #getMaxPriority + */ + public final void setMaxPriority(int newMax) { + if (newMax <= this.maxPriority) { + if (newMax < Thread.MIN_PRIORITY) { + newMax = Thread.MIN_PRIORITY; + } + + int parentPriority = parent == null ? newMax : parent.getMaxPriority(); + this.maxPriority = parentPriority <= newMax ? parentPriority : newMax; + synchronized (groups) { + for (ThreadGroup group : groups) { + group.setMaxPriority(newMax); + } + } + } + } + + /** + * Stops every thread in this group and recursively in all its subgroups. + * + * @see Thread#stop() + * @see Thread#stop(Throwable) + * @see ThreadDeath + * + * @deprecated Requires deprecated method {@link Thread#stop()}. + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void stop() { + if (stopHelper()) { + Thread.currentThread().stop(); + } + } + + @SuppressWarnings("deprecation") + private boolean stopHelper() { + boolean stopCurrent = false; + synchronized (threadRefs) { + Thread current = Thread.currentThread(); + for (Thread thread : threads) { + if (thread == current) { + stopCurrent = true; + } else { + thread.stop(); + } + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + stopCurrent |= group.stopHelper(); + } + } + return stopCurrent; + } + + /** + * Suspends every thread in this group and recursively in all its + * subgroups. + * + * @see Thread#suspend + * @see #resume + * + * @deprecated Requires deprecated method {@link Thread#suspend()}. + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void suspend() { + if (suspendHelper()) { + Thread.currentThread().suspend(); + } + } + + @SuppressWarnings("deprecation") + private boolean suspendHelper() { + boolean suspendCurrent = false; + synchronized (threadRefs) { + Thread current = Thread.currentThread(); + for (Thread thread : threads) { + if (thread == current) { + suspendCurrent = true; + } else { + thread.suspend(); + } + } + } + synchronized (groups) { + for (ThreadGroup group : groups) { + suspendCurrent |= group.suspendHelper(); + } + } + return suspendCurrent; + } + + @Override + public String toString() { + return getClass().getName() + "[name=" + getName() + + ",maxPriority=" + getMaxPriority() + "]"; + } + + /** + * Handles uncaught exceptions. Any uncaught exception in any {@code Thread} + * is forwarded to the thread's {@code ThreadGroup} by invoking this + * method. + * + * <p>New code should use {@link Thread#setUncaughtExceptionHandler} instead of thread groups. + * + * @param t the Thread that terminated with an uncaught exception + * @param e the uncaught exception itself + */ + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + parent.uncaughtException(t, e); + } else if (Thread.getDefaultUncaughtExceptionHandler() != null) { + // TODO The spec is unclear regarding this. What do we do? + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e); + } else if (!(e instanceof ThreadDeath)) { + // No parent group, has to be 'system' Thread Group + e.printStackTrace(System.err); + } + } + + /** + * Called by the Thread constructor. + */ + final void addThread(Thread thread) throws IllegalThreadStateException { + synchronized (threadRefs) { + if (isDestroyed) { + throw new IllegalThreadStateException(); + } + threadRefs.add(new WeakReference<Thread>(thread)); + } + } + + /** + * Called by the VM when a Thread dies. + */ + final void removeThread(Thread thread) throws IllegalThreadStateException { + synchronized (threadRefs) { + for (Iterator<Thread> i = threads.iterator(); i.hasNext(); ) { + if (i.next().equals(thread)) { + i.remove(); + break; + } + } + } + destroyIfEmptyDaemon(); + } +} diff --git a/libdvm/src/main/java/java/lang/VMClassLoader.java b/libdvm/src/main/java/java/lang/VMClassLoader.java new file mode 100644 index 0000000..d7162c6 --- /dev/null +++ b/libdvm/src/main/java/java/lang/VMClassLoader.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +class VMClassLoader { + + /** + * Get a resource from a file in the bootstrap class path. + * + * It would be simpler to just walk through the class path elements + * ourselves, but that would require reopening Jar files. + * + * We assume that the bootclasspath can't change once the VM has + * started. This assumption seems to be supported by the spec. + */ + static URL getResource(String name) { + int numEntries = getBootClassPathSize(); + for (int i = 0; i < numEntries; i++) { + String urlStr = getBootClassPathResource(name, i); + if (urlStr != null) { + try { + return new URL(urlStr); + } catch (MalformedURLException mue) { + mue.printStackTrace(); + // unexpected; keep going + } + } + } + return null; + } + + /* + * Get an enumeration with all matching resources. + */ + static List<URL> getResources(String name) { + ArrayList<URL> list = new ArrayList<URL>(); + int numEntries = getBootClassPathSize(); + for (int i = 0; i < numEntries; i++) { + String urlStr = getBootClassPathResource(name, i); + if (urlStr != null) { + try { + list.add(new URL(urlStr)); + } catch (MalformedURLException mue) { + mue.printStackTrace(); + // unexpected; keep going + } + } + } + return list; + } + + /** + * Load class with bootstrap class loader. + */ + native static Class loadClass(String name, boolean resolve) throws ClassNotFoundException; + + native static Class getPrimitiveClass(char type); + + native static Class findLoadedClass(ClassLoader cl, String name); + + /** + * Boot class path manipulation, for getResources(). + */ + native private static int getBootClassPathSize(); + native private static String getBootClassPathResource(String name, int index); +} diff --git a/libdvm/src/main/java/java/lang/VMThread.java b/libdvm/src/main/java/java/lang/VMThread.java new file mode 100644 index 0000000..01f6ee3 --- /dev/null +++ b/libdvm/src/main/java/java/lang/VMThread.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +class VMThread { + Thread thread; + int vmData; + + VMThread(Thread t) { + thread = t; + } + + native static void create(Thread t, long stackSize); + + static native Thread currentThread(); + static native boolean interrupted(); + static native void sleep (long msec, int nsec) throws InterruptedException; + static native void yield(); + + native void interrupt(); + + native boolean isInterrupted(); + + /** + * Starts the VMThread (and thus the Java Thread) with the given + * stack size. + */ + void start(long stackSize) { + VMThread.create(thread, stackSize); + } + + /** + * Queries whether this Thread holds a monitor lock on the + * given object. + */ + native boolean holdsLock(Object object); + + native void setPriority(int newPriority); + native int getStatus(); + + /** + * Holds a mapping from native Thread statuses to Java one. Required for + * translating back the result of getStatus(). + */ + static final Thread.State[] STATE_MAP = new Thread.State[] { + Thread.State.TERMINATED, // ZOMBIE + Thread.State.RUNNABLE, // RUNNING + Thread.State.TIMED_WAITING, // TIMED_WAIT + Thread.State.BLOCKED, // MONITOR + Thread.State.WAITING, // WAIT + Thread.State.NEW, // INITIALIZING + Thread.State.NEW, // STARTING + Thread.State.RUNNABLE, // NATIVE + Thread.State.WAITING, // VMWAIT + Thread.State.RUNNABLE // SUSPENDED + }; + + /** + * Tell the VM that the thread's name has changed. This is useful for + * DDMS, which would otherwise be oblivious to Thread.setName calls. + */ + native void nameChanged(String newName); +} diff --git a/libdvm/src/main/java/java/lang/ref/Reference.java b/libdvm/src/main/java/java/lang/ref/Reference.java new file mode 100644 index 0000000..bd63535 --- /dev/null +++ b/libdvm/src/main/java/java/lang/ref/Reference.java @@ -0,0 +1,200 @@ +/* + * 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. + * + * <p>Three different type of references exist, each being weaker than the preceding one: + * {@link java.lang.ref.SoftReference}, {@link java.lang.ref.WeakReference}, and + * {@link java.lang.ref.PhantomReference}. "Weakness" here means that less restrictions are + * being imposed on the garbage collector as to when it is allowed to + * actually garbage-collect the referenced object. + * + * <p>In order to use reference objects properly it is important to understand + * the different types of reachability that trigger their clearing and + * enqueueing. The following table lists these, from strongest to weakest. + * For each row, an object is said to have the reachability on the left side + * if (and only if) it fulfills all of the requirements on the right side. In + * all rows, consider the <em>root set</em> to be a set of references that + * are "resistant" to garbage collection (that is, running threads, method + * parameters, local variables, static fields and the like). + * + * <p><table> + * <tr> + * <td>Strongly reachable</td> + * <td> <ul> + * <li>There exists at least one path from the root set to the object that does not traverse any + * instance of a {@code java.lang.ref.Reference} subclass. + * </li> + * </ul> </td> + * </tr> + * + * <tr> + * <td>Softly reachable</td> + * <td> <ul> + * <li>The object is not strongly reachable.</li> + * <li>There exists at least one path from the root set to the object that does traverse + * a {@code java.lang.ref.SoftReference} instance, but no {@code java.lang.ref.WeakReference} + * or {@code java.lang.ref.PhantomReference} instances.</li> + * </ul> </td> + * </tr> + * + * <tr> + * <td>Weakly reachable</td> + * <td> <ul> + * <li>The object is neither strongly nor softly reachable.</li> + * <li>There exists at least one path from the root set to the object that does traverse a + * {@code java.lang.ref.WeakReference} instance, but no {@code java.lang.ref.PhantomReference} + * instances.</li> + * </ul> </td> + * </tr> + * + * <tr> + * <td>Phantom-reachable</td> + * <td> <ul> + * <li>The object is neither strongly, softly, nor weakly reachable.</li> + * <li>The object is referenced by a {@code java.lang.ref.PhantomReference} instance.</li> + * <li>The object has already been finalized.</li> + * </ul> </td> + * </tr> + * </table> + */ +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. + */ + volatile 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. + */ + volatile ReferenceQueue<? super T> 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") + volatile Reference queueNext; + + /** + * Used internally by the VM. This field forms a circular and + * singly linked list of reference objects discovered by the + * garbage collector and awaiting processing by the reference + * queue thread. + * + * @hide + */ + public volatile Reference<?> pendingNext; + + /** + * Constructs a new instance of this class. + */ + Reference() { + } + + Reference(T r, ReferenceQueue<? super T> q) { + referent = r; + queue = q; + } + + /** + * Makes the referent {@code null}. This does not force the reference + * object to be enqueued. + */ + public void clear() { + referent = null; + } + + /** + * Adds an object to its reference queue. + * + * @return {@code true} if this call has caused the {@code Reference} to + * become enqueued, or {@code false} otherwise + * + * @hide + */ + public final synchronized boolean enqueueInternal() { + 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 + */ + 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. + */ + 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 + */ + public boolean isEnqueued() { + return queueNext != null; + } + +} diff --git a/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java b/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java new file mode 100644 index 0000000..f3d96af --- /dev/null +++ b/libdvm/src/main/java/java/lang/reflect/AccessibleObject.java @@ -0,0 +1,320 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; +import java.util.Hashtable; +import org.apache.harmony.kernel.vm.StringUtils; + +/** + * {@code AccessibleObject} is the superclass of all member reflection classes + * (Field, Constructor, Method). AccessibleObject provides the ability to toggle + * a flag controlling access checks for these objects. By default, accessing a + * member (for example, setting a field or invoking a method) checks the + * validity of the access (for example, invoking a private method from outside + * the defining class is prohibited) and throws IllegalAccessException if the + * operation is not permitted. If the accessible flag is set to true, these + * checks are omitted. This allows privileged code, such as Java object + * serialization, object inspectors, and debuggers to have complete access to + * objects. + * + * @see Field + * @see Constructor + * @see Method + */ +public class AccessibleObject implements AnnotatedElement { + + /** + * If true, object is accessible, bypassing normal access checks + */ + boolean flag = false; + + // Holds a mapping from Java type names to native type codes. + static Hashtable<String, String> trans; + + static { + trans = new Hashtable<String, String>(9); + trans.put("byte", "B"); + trans.put("char", "C"); + trans.put("short", "S"); + trans.put("int", "I"); + trans.put("long", "J"); + trans.put("float", "F"); + trans.put("double", "D"); + trans.put("void", "V"); + trans.put("boolean", "Z"); + } + + /** + * Attempts to set the value of the accessible flag for all the objects in + * the array provided. Setting this + * flag to {@code false} will enable access checks, setting to {@code true} + * will disable them. + * + * @param objects + * the accessible objects + * @param flag + * the new value for the accessible flag + * + * @see #setAccessible(boolean) + */ + public static void setAccessible(AccessibleObject[] objects, boolean flag) { + for (AccessibleObject object : objects) { + object.flag = flag; + } + } + + /** + * Constructs a new {@code AccessibleObject} instance. {@code + * AccessibleObject} instances can only be constructed by the virtual + * machine. + */ + protected AccessibleObject() { + } + + /** + * Indicates whether this object is accessible without access checks being + * performed. Returns the accessible flag. + * + * @return {@code true} if this object is accessible without access + * checks, {@code false} otherwise + */ + public boolean isAccessible() { + return flag; + } + + /** + * Attempts to set the value of the accessible flag. Setting this flag to + * {@code false} will enable access checks, setting to {@code true} will + * disable them. + * + * @param flag + * the new value for the accessible flag + */ + public void setAccessible(boolean flag) { + this.flag = flag; + } + + public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { + throw new UnsupportedOperationException(); + } + + public Annotation[] getDeclaredAnnotations() { + throw new UnsupportedOperationException(); + } + + public Annotation[] getAnnotations() { + // for all but Class, getAnnotations == getDeclaredAnnotations + return getDeclaredAnnotations(); + } + + public <T extends Annotation> T getAnnotation(Class<T> annotationType) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the signature for a class. This is the kind of signature used + * internally by the JVM, with one-character codes representing the basic + * types. It is not suitable for printing. + * + * @param clazz + * the class for which a signature is required + * + * @return The signature as a string + */ + String getSignature(Class<?> clazz) { + String result = ""; + String nextType = clazz.getName(); + + if(trans.containsKey(nextType)) { + result = trans.get(nextType); + } else { + if(clazz.isArray()) { + result = "[" + getSignature(clazz.getComponentType()); + } else { + result = "L" + nextType + ";"; + } + } + return result; + } + + /** + * Returns a printable String consisting of the canonical names of the + * classes contained in an array. The form is that used in parameter and + * exception lists, that is, the class or type names are separated by + * commas. + * + * @param types + * the array of classes + * + * @return The String of names + */ + String toString(Class<?>[] types) { + StringBuilder result = new StringBuilder(); + + if (types.length != 0) { + appendTypeName(result, types[0]); + for (int i = 1; i < types.length; i++) { + result.append(','); + appendTypeName(result, types[i]); + } + } + + return result.toString(); + } + + /** + * Gets the Signature attribute for this instance. Returns {@code null} + * if not found. + */ + /*package*/ String getSignatureAttribute() { + /* + * Note: This method would have been declared abstract, but the + * standard API lists this class as concrete. + */ + throw new UnsupportedOperationException(); + } + + /** + * Retrieve the signature attribute from an arbitrary class. This is + * the same as Class.getSignatureAttribute(), but it can be used from + * the java.lang.reflect package. + */ + /*package*/ static String getClassSignatureAttribute(Class clazz) { + Object[] annotation = getClassSignatureAnnotation(clazz); + + if (annotation == null) { + return null; + } + + return StringUtils.combineStrings(annotation); + } + + /** + * Retrieve the signature annotation from an arbitrary class. This is + * the same as Class.getSignatureAttribute(), but it can be used from + * the java.lang.reflect package. + */ + private static native Object[] getClassSignatureAnnotation(Class clazz); + + /** + * Appends the best {@link #toString} name for {@code c} to {@code out}. + * This works around the fact that {@link Class#getName} is lousy for + * primitive arrays (it writes "[C" instead of "char[]") and {@link + * Class#getCanonicalName()} is lousy for nested classes (it uses a "." + * separator rather than a "$" separator). + */ + void appendTypeName(StringBuilder out, Class<?> c) { + int dimensions = 0; + while (c.isArray()) { + c = c.getComponentType(); + dimensions++; + } + out.append(c.getName()); + for (int d = 0; d < dimensions; d++) { + out.append("[]"); + } + } + + /** + * Appends names of the specified array classes to the buffer. The array + * elements may represent a simple type, a reference type or an array type. + * Output format: java.lang.Object[], java.io.File, void + * + * @param types array of classes to print the names + * @throws NullPointerException if any of the arguments is null + */ + void appendArrayGenericType(StringBuilder sb, Type[] types) { + if (types.length > 0) { + appendGenericType(sb, types[0]); + for (int i = 1; i < types.length; i++) { + sb.append(','); + appendGenericType(sb, types[i]); + } + } + } + + /** + * Appends the generic type representation to the buffer. + * + * @param sb buffer + * @param obj the generic type which representation should be appended to the buffer + * + * @throws NullPointerException if any of the arguments is null + */ + void appendGenericType(StringBuilder sb, Type obj) { + if (obj instanceof TypeVariable) { + sb.append(((TypeVariable)obj).getName()); + } else if (obj instanceof ParameterizedType) { + sb.append(obj.toString()); + } else if (obj instanceof GenericArrayType) { //XXX: is it a working branch? + Type simplified = ((GenericArrayType)obj).getGenericComponentType(); + appendGenericType(sb, simplified); + sb.append("[]"); + } else if (obj instanceof Class) { + Class c = ((Class<?>)obj); + if (c.isArray()){ + String as[] = c.getName().split("\\["); + int len = as.length-1; + if (as[len].length() > 1){ + sb.append(as[len].substring(1, as[len].length()-1)); + } else { + char ch = as[len].charAt(0); + if (ch == 'I') + sb.append("int"); + else if (ch == 'B') + sb.append("byte"); + else if (ch == 'J') + sb.append("long"); + else if (ch == 'F') + sb.append("float"); + else if (ch == 'D') + sb.append("double"); + else if (ch == 'S') + sb.append("short"); + else if (ch == 'C') + sb.append("char"); + else if (ch == 'Z') + sb.append("boolean"); + else if (ch == 'V') //XXX: is it a working branch? + sb.append("void"); + } + for (int i = 0; i < len; i++){ + sb.append("[]"); + } + } else { + sb.append(c.getName()); + } + } + } +} diff --git a/libdvm/src/main/java/java/lang/reflect/Constructor.java b/libdvm/src/main/java/java/lang/reflect/Constructor.java new file mode 100644 index 0000000..acce44f --- /dev/null +++ b/libdvm/src/main/java/java/lang/reflect/Constructor.java @@ -0,0 +1,466 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; +import libcore.util.EmptyArray; +import org.apache.harmony.kernel.vm.StringUtils; +import libcore.reflect.GenericSignatureParser; +import libcore.reflect.ListOfTypes; +import libcore.reflect.Types; + +/** + * This class represents a constructor. Information about the constructor can be + * accessed, and the constructor can be invoked dynamically. + * + * @param <T> the class that declares this constructor + */ +public final class Constructor<T> extends AccessibleObject implements GenericDeclaration, + Member { + + Class<T> declaringClass; + + Class<?>[] parameterTypes; + + Class<?>[] exceptionTypes; + + ListOfTypes genericExceptionTypes; + ListOfTypes genericParameterTypes; + TypeVariable<Constructor<T>>[] formalTypeParameters; + private volatile boolean genericTypesAreInitialized = false; + + private synchronized void initGenericTypes() { + if (!genericTypesAreInitialized) { + String signatureAttribute = getSignatureAttribute(); + GenericSignatureParser parser = new GenericSignatureParser( + declaringClass.getClassLoader()); + parser.parseForConstructor(this, signatureAttribute, exceptionTypes); + formalTypeParameters = parser.formalTypeParameters; + genericParameterTypes = parser.parameterTypes; + genericExceptionTypes = parser.exceptionTypes; + genericTypesAreInitialized = true; + } + } + + int slot; + + private int methodDexIndex; + + /** + * 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, int methodDexIndex) { + this.declaringClass = declaringClass; + this.parameterTypes = ptypes; + this.exceptionTypes = extypes; // may be null + this.slot = slot; + this.methodDexIndex = methodDexIndex; + } + + /** @hide */ + public int getDexMethodIndex() { + return methodDexIndex; + } + + @Override /*package*/ String getSignatureAttribute() { + Object[] annotation = Method.getSignatureAnnotation(declaringClass, slot); + + if (annotation == null) { + return null; + } + + return StringUtils.combineStrings(annotation); + } + + public TypeVariable<Constructor<T>>[] getTypeParameters() { + initGenericTypes(); + return formalTypeParameters.clone(); + } + + /** + * Returns the string representation of the constructor's declaration, + * including the type parameters. + * + * @return the string representation of the constructor's declaration + */ + public String toGenericString() { + StringBuilder sb = new StringBuilder(80); + initGenericTypes(); + // append modifiers if any + int modifier = getModifiers(); + if (modifier != 0) { + sb.append(Modifier.toString(modifier & ~Modifier.VARARGS)).append(' '); + } + // append type parameters + if (formalTypeParameters != null && formalTypeParameters.length > 0) { + sb.append('<'); + for (int i = 0; i < formalTypeParameters.length; i++) { + appendGenericType(sb, formalTypeParameters[i]); + if (i < formalTypeParameters.length - 1) { + sb.append(","); + } + } + sb.append("> "); + } + // append constructor name + appendTypeName(sb, getDeclaringClass()); + // append parameters + sb.append('('); + appendArrayGenericType(sb, Types.getTypeArray(genericParameterTypes, false)); + sb.append(')'); + // append exceptions if any + Type[] genericExceptionTypeArray = Types.getTypeArray(genericExceptionTypes, false); + if (genericExceptionTypeArray.length > 0) { + sb.append(" throws "); + appendArrayGenericType(sb, genericExceptionTypeArray); + } + return sb.toString(); + } + + /** + * Returns the generic parameter types as an array of {@code Type} + * instances, in declaration order. If this constructor has no generic + * parameters, an empty array is returned. + * + * @return the parameter types + * + * @throws GenericSignatureFormatError + * if the generic constructor signature is invalid + * @throws TypeNotPresentException + * if any parameter type points to a missing type + * @throws MalformedParameterizedTypeException + * if any parameter type points to a type that cannot be + * instantiated for some reason + */ + public Type[] getGenericParameterTypes() { + initGenericTypes(); + return Types.getTypeArray(genericParameterTypes, true); + } + + /** + * Returns the exception types as an array of {@code Type} instances. If + * this constructor has no declared exceptions, an empty array will be + * returned. + * + * @return an array of generic exception types + * + * @throws GenericSignatureFormatError + * if the generic constructor signature is invalid + * @throws TypeNotPresentException + * if any exception type points to a missing type + * @throws MalformedParameterizedTypeException + * if any exception type points to a type that cannot be + * instantiated for some reason + */ + public Type[] getGenericExceptionTypes() { + initGenericTypes(); + return Types.getTypeArray(genericExceptionTypes, true); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return Method.getDeclaredAnnotations(declaringClass, slot); + } + + @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + return Method.getAnnotation(declaringClass, slot, annotationType); + } + + @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + return Method.isAnnotationPresent(declaringClass, slot, annotationType); + } + + /** + * Returns an array of arrays that represent the annotations of the formal + * parameters of this constructor. If there are no parameters on this + * constructor, then an empty array is returned. If there are no annotations + * set, then an array of empty arrays is returned. + * + * @return an array of arrays of {@code Annotation} instances + */ + public Annotation[][] getParameterAnnotations() { + Annotation[][] parameterAnnotations + = Method.getParameterAnnotations(declaringClass, slot); + if (parameterAnnotations.length == 0) { + return Method.noAnnotations(parameterTypes.length); + } + return parameterAnnotations; + } + + /** + * Indicates whether or not this constructor takes a variable number of + * arguments. + * + * @return {@code true} if a vararg is declare, otherwise + * {@code false} + */ + public boolean isVarArgs() { + int mods = Method.getMethodModifiers(declaringClass, slot); + return (mods & Modifier.VARARGS) != 0; + } + + /** + * Indicates whether or not this constructor is synthetic (artificially + * introduced by the compiler). + * + * @return {@code true} if this constructor is synthetic, {@code false} + * otherwise + */ + public boolean isSynthetic() { + int mods = Method.getMethodModifiers(declaringClass, slot); + return (mods & Modifier.SYNTHETIC) != 0; + } + + /** + * Indicates whether or not the specified {@code object} is equal to this + * constructor. To be equal, the specified object must be an instance + * of {@code Constructor} with the same declaring class and parameter types + * as this constructor. + * + * @param object + * the object to compare + * + * @return {@code true} if the specified object is equal to this + * constructor, {@code false} otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + return object instanceof Constructor && toString().equals(object.toString()); + } + + /** + * Returns the class that declares this constructor. + * + * @return the declaring class + */ + public Class<T> getDeclaringClass() { + return declaringClass; + } + + /** + * Returns the exception types as an array of {@code Class} instances. If + * this constructor has no declared exceptions, an empty array will be + * returned. + * + * @return the declared exception classes + */ + public Class<?>[] getExceptionTypes() { + if (exceptionTypes == null) { + return EmptyArray.CLASS; + } + return exceptionTypes.clone(); + } + + /** + * Returns the modifiers for this constructor. The {@link Modifier} class + * should be used to decode the result. + * + * @return the modifiers for this constructor + * + * @see Modifier + */ + public int getModifiers() { + return Method.getMethodModifiers(declaringClass, slot); + } + + /** + * Returns the name of this constructor. + * + * @return the name of this constructor + */ + public String getName() { + return declaringClass.getName(); + } + + /** + * Returns an array of the {@code Class} objects associated with the + * parameter types of this constructor. If the constructor was declared with + * no parameters, an empty array will be returned. + * + * @return the parameter types + */ + public Class<?>[] getParameterTypes() { + return parameterTypes.clone(); + } + + /** + * Returns the constructor's signature in non-printable form. This is called + * (only) from IO native code and needed for deriving the serialVersionUID + * of the class + * + * @return the constructor's signature + */ + @SuppressWarnings("unused") + private String getSignature() { + StringBuilder result = new StringBuilder(); + + result.append('('); + for (int i = 0; i < parameterTypes.length; i++) { + result.append(getSignature(parameterTypes[i])); + } + result.append(")V"); + + return result.toString(); + } + + /** + * Returns an integer hash code for this constructor. Constructors which are + * equal return the same value for this method. The hash code for a + * Constructor is the hash code of the name of the declaring class. + * + * @return the hash code + * + * @see #equals + */ + @Override + public int hashCode() { + return declaringClass.getName().hashCode(); + } + + /** + * Returns a new instance of the declaring class, initialized by dynamically + * invoking the constructor represented by this {@code Constructor} object. + * This reproduces the effect of {@code new declaringClass(arg1, arg2, ... , + * argN)} This method performs the following: + * <ul> + * <li>A new instance of the declaring class is created. If the declaring + * class cannot be instantiated (i.e. abstract class, an interface, an array + * type, or a primitive type) then an InstantiationException is thrown.</li> + * <li>If this Constructor object is enforcing access control (see + * {@link AccessibleObject}) and this constructor is not accessible from the + * current context, an IllegalAccessException is thrown.</li> + * <li>If the number of arguments passed and the number of parameters do not + * match, an IllegalArgumentException is thrown.</li> + * <li>For each argument passed: + * <ul> + * <li>If the corresponding parameter type is a primitive type, the argument + * is unboxed. If the unboxing fails, an IllegalArgumentException is + * thrown.</li> + * <li>If the resulting argument cannot be converted to the parameter type + * via a widening conversion, an IllegalArgumentException is thrown.</li> + * </ul> + * <li>The constructor represented by this {@code Constructor} object is + * then invoked. If an exception is thrown during the invocation, it is + * caught and wrapped in an InvocationTargetException. This exception is + * then thrown. If the invocation completes normally, the newly initialized + * object is returned. + * </ul> + * + * @param args + * the arguments to the constructor + * + * @return the new, initialized, object + * + * @throws InstantiationException + * if the class cannot be instantiated + * @throws IllegalAccessException + * if this constructor is not accessible + * @throws IllegalArgumentException + * if an incorrect number of arguments are passed, or an + * argument could not be converted by a widening conversion + * @throws InvocationTargetException + * if an exception was thrown by the invoked constructor + * + * @see AccessibleObject + */ + public T newInstance(Object... args) throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException { + return constructNative (args, declaringClass, parameterTypes, slot, flag); + } + + private native T constructNative(Object[] args, Class<T> declaringClass, + Class<?>[] parameterTypes, int slot, + boolean noAccessCheck) throws InstantiationException, IllegalAccessException, + InvocationTargetException; + + /** + * Returns a string containing a concise, human-readable description of this + * constructor. The format of the string is: + * + * <ol> + * <li>modifiers (if any) + * <li>declaring class name + * <li>'(' + * <li>parameter types, separated by ',' (if any) + * <li>')' + * <li>'throws' plus exception types, separated by ',' (if any) + * </ol> + * + * For example: + * {@code public String(byte[],String) throws UnsupportedEncodingException} + * + * @return a printable representation for this constructor + */ + @Override + public String toString() { + StringBuilder result = new StringBuilder(Modifier.toString(getModifiers())); + + if (result.length() != 0) + result.append(' '); + result.append(declaringClass.getName()); + result.append("("); + result.append(toString(parameterTypes)); + result.append(")"); + if (exceptionTypes != null && exceptionTypes.length != 0) { + result.append(" throws "); + result.append(toString(exceptionTypes)); + } + + return result.toString(); + } +} diff --git a/libdvm/src/main/java/java/lang/reflect/Field.java b/libdvm/src/main/java/java/lang/reflect/Field.java new file mode 100644 index 0000000..fa8e5e7 --- /dev/null +++ b/libdvm/src/main/java/java/lang/reflect/Field.java @@ -0,0 +1,931 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; +import java.util.Comparator; +import org.apache.harmony.kernel.vm.StringUtils; +import libcore.reflect.GenericSignatureParser; +import libcore.reflect.Types; + +/** + * This class represents a field. Information about the field can be accessed, + * and the field's value can be accessed dynamically. + */ +public final class Field extends AccessibleObject implements Member { + + /** + * Orders fields by their name and declaring class. + * + * @hide + */ + public static final Comparator<Field> ORDER_BY_NAME_AND_DECLARING_CLASS + = new Comparator<Field>() { + @Override public int compare(Field a, Field b) { + int comparison = a.name.compareTo(b.name); + if (comparison != 0) { + return comparison; + } + + return a.getDeclaringClass().getName().compareTo(b.getDeclaringClass().getName()); + } + }; + + private Class<?> declaringClass; + + private Class<?> type; + + private Type genericType; + + private volatile boolean genericTypesAreInitialized = false; + + private String name; + + private int slot; + + private final int fieldDexIndex; + + private static final char TYPE_BOOLEAN = 'Z'; + + private static final char TYPE_BYTE = 'B'; + + private static final char TYPE_CHAR = 'C'; + + private static final char TYPE_SHORT = 'S'; + + private static final char TYPE_INTEGER = 'I'; + + private static final char TYPE_FLOAT = 'F'; + + private static final char TYPE_LONG = 'J'; + + private static final char TYPE_DOUBLE = 'D'; + + private Field(Class<?> declaringClass, Class<?> type, String name, int slot, int fieldDexIndex) { + this.declaringClass = declaringClass; + this.type = type; + this.name = name; + this.slot = slot; + this.fieldDexIndex = fieldDexIndex; + } + + /** + * Returns the index of this field's ID in its dex file. + * @hide + */ + public int getDexFieldIndex() { + return fieldDexIndex; + } + + private synchronized void initGenericType() { + if (!genericTypesAreInitialized) { + String signatureAttribute = getSignatureAttribute(); + GenericSignatureParser parser = new GenericSignatureParser( + declaringClass.getClassLoader()); + parser.parseForField(this.declaringClass, signatureAttribute); + genericType = parser.fieldType; + if (genericType == null) { + genericType = getType(); + } + genericTypesAreInitialized = true; + } + } + + /** {@inheritDoc} */ + @Override + /* package */String getSignatureAttribute() { + Object[] annotation = getSignatureAnnotation(declaringClass, slot); + + if (annotation == null) { + return null; + } + + return StringUtils.combineStrings(annotation); + } + + /** + * Get the Signature annotation for this field. Returns null if not found. + */ + native private Object[] getSignatureAnnotation(Class declaringClass, int slot); + + /** + * Indicates whether or not this field is synthetic. + * + * @return {@code true} if this field is synthetic, {@code false} otherwise + */ + public boolean isSynthetic() { + int flags = getFieldModifiers(declaringClass, slot); + return (flags & Modifier.SYNTHETIC) != 0; + } + + /** + * Returns the string representation of this field, including the field's + * generic type. + * + * @return the string representation of this field + */ + public String toGenericString() { + StringBuilder sb = new StringBuilder(80); + // append modifiers if any + int modifier = getModifiers(); + if (modifier != 0) { + sb.append(Modifier.toString(modifier)).append(' '); + } + // append generic type + appendGenericType(sb, getGenericType()); + sb.append(' '); + // append full field name + sb.append(getDeclaringClass().getName()).append('.').append(getName()); + return sb.toString(); + } + + /** + * Indicates whether or not this field is an enumeration constant. + * + * @return {@code true} if this field is an enumeration constant, {@code + * false} otherwise + */ + public boolean isEnumConstant() { + int flags = getFieldModifiers(declaringClass, slot); + return (flags & Modifier.ENUM) != 0; + } + + /** + * Returns the generic type of this field. + * + * @return the generic type + * @throws GenericSignatureFormatError + * if the generic field signature is invalid + * @throws TypeNotPresentException + * if the generic type points to a missing type + * @throws MalformedParameterizedTypeException + * if the generic type points to a type that cannot be + * instantiated for some reason + */ + public Type getGenericType() { + initGenericType(); + return Types.getType(genericType); + } + + @Override public Annotation[] getDeclaredAnnotations() { + return getDeclaredAnnotations(declaringClass, slot); + } + private static native Annotation[] getDeclaredAnnotations(Class declaringClass, int slot); + + @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + return getAnnotation(declaringClass, slot, annotationType); + } + private static native <A extends Annotation> A getAnnotation( + Class<?> declaringClass, int slot, Class<A> annotationType); + + @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + return isAnnotationPresent(declaringClass, slot, annotationType); + } + private static native boolean isAnnotationPresent( + Class<?> declaringClass, int slot, Class<? extends Annotation> annotationType); + + /** + * Indicates whether or not the specified {@code object} is equal to this + * field. To be equal, the specified object must be an instance of + * {@code Field} with the same declaring class, type and name as this field. + * + * @param object + * the object to compare + * @return {@code true} if the specified object is equal to this method, + * {@code false} otherwise + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + return object instanceof Field && toString().equals(object.toString()); + } + + /** + * Returns the value of the field in the specified object. This reproduces + * the effect of {@code object.fieldName} + * + * <p>If the type of this field is a primitive type, the field value is + * automatically boxed. + * + * <p>If this field is static, the object argument is ignored. + * Otherwise, if the object is null, a NullPointerException is thrown. If + * the object is not an instance of the declaring class of the method, an + * IllegalArgumentException is thrown. + * + * <p>If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value, possibly boxed + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public Object get(Object object) throws IllegalAccessException, IllegalArgumentException { + return getField(object, declaringClass, type, slot, flag); + } + + /** + * Returns the value of the field in the specified object as a {@code + * boolean}. This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public boolean getBoolean(Object object) throws IllegalAccessException, + IllegalArgumentException { + return getZField(object, declaringClass, type, slot, flag, TYPE_BOOLEAN); + } + + /** + * Returns the value of the field in the specified object as a {@code byte}. + * This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public byte getByte(Object object) throws IllegalAccessException, IllegalArgumentException { + return getBField(object, declaringClass, type, slot, flag, TYPE_BYTE); + } + + /** + * Returns the value of the field in the specified object as a {@code char}. + * This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public char getChar(Object object) throws IllegalAccessException, IllegalArgumentException { + return getCField(object, declaringClass, type, slot, flag, TYPE_CHAR); + } + + /** + * Returns the class that declares this field. + * + * @return the declaring class + */ + public Class<?> getDeclaringClass() { + return declaringClass; + } + + /** + * Returns the value of the field in the specified object as a {@code + * double}. This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public double getDouble(Object object) throws IllegalAccessException, IllegalArgumentException { + return getDField(object, declaringClass, type, slot, flag, TYPE_DOUBLE); + } + + /** + * Returns the value of the field in the specified object as a {@code float} + * . This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public float getFloat(Object object) throws IllegalAccessException, IllegalArgumentException { + return getFField(object, declaringClass, type, slot, flag, TYPE_FLOAT); + } + + /** + * Returns the value of the field in the specified object as an {@code int}. + * This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public int getInt(Object object) throws IllegalAccessException, IllegalArgumentException { + return getIField(object, declaringClass, type, slot, flag, TYPE_INTEGER); + } + + /** + * Returns the value of the field in the specified object as a {@code long}. + * This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public long getLong(Object object) throws IllegalAccessException, IllegalArgumentException { + return getJField(object, declaringClass, type, slot, flag, TYPE_LONG); + } + + /** + * Returns the modifiers for this field. The {@link Modifier} class should + * be used to decode the result. + * + * @return the modifiers for this field + * @see Modifier + */ + public int getModifiers() { + return getFieldModifiers(declaringClass, slot); + } + + private native int getFieldModifiers(Class<?> declaringClass, int slot); + + /** + * Returns the name of this field. + * + * @return the name of this field + */ + public String getName() { + return name; + } + + /** + * Returns the value of the field in the specified object as a {@code short} + * . This reproduces the effect of {@code object.fieldName} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * @param object + * the object to access + * @return the field value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public short getShort(Object object) throws IllegalAccessException, IllegalArgumentException { + return getSField(object, declaringClass, type, slot, flag, TYPE_SHORT); + } + + /** + * Returns the constructor's signature in non-printable form. This is called + * (only) from IO native code and needed for deriving the serialVersionUID + * of the class + * + * @return the constructor's signature. + */ + @SuppressWarnings("unused") + private String getSignature() { + return getSignature(type); + } + + /** + * Return the {@link Class} associated with the type of this field. + * + * @return the type of this field + */ + public Class<?> getType() { + return type; + } + + /** + * Returns an integer hash code for this field. Objects which are equal + * return the same value for this method. + * <p> + * The hash code for a Field is the exclusive-or combination of the hash + * code of the field's name and the hash code of the name of its declaring + * class. + * + * @return the hash code for this field + * @see #equals + */ + @Override + public int hashCode() { + return name.hashCode() ^ getDeclaringClass().getName().hashCode(); + } + + /** + * Sets the value of the field in the specified object to the value. This + * reproduces the effect of {@code object.fieldName = value} + * + * <p>If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * + * <p>If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * + * <p>If the field type is a primitive type, the value is automatically + * unboxed. If the unboxing fails, an IllegalArgumentException is thrown. If + * the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void set(Object object, Object value) throws IllegalAccessException, + IllegalArgumentException { + setField(object, declaringClass, type, slot, flag, value); + } + + /** + * Sets the value of the field in the specified object to the {@code + * boolean} value. This reproduces the effect of {@code object.fieldName = + * value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setBoolean(Object object, boolean value) throws IllegalAccessException, + IllegalArgumentException { + setZField(object, declaringClass, type, slot, flag, TYPE_BOOLEAN, value); + } + + /** + * Sets the value of the field in the specified object to the {@code byte} + * value. This reproduces the effect of {@code object.fieldName = value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setByte(Object object, byte value) throws IllegalAccessException, + IllegalArgumentException { + setBField(object, declaringClass, type, slot, flag, TYPE_BYTE, value); + } + + /** + * Sets the value of the field in the specified object to the {@code char} + * value. This reproduces the effect of {@code object.fieldName = value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setChar(Object object, char value) throws IllegalAccessException, + IllegalArgumentException { + setCField(object, declaringClass, type, slot, flag, TYPE_CHAR, value); + } + + /** + * Sets the value of the field in the specified object to the {@code double} + * value. This reproduces the effect of {@code object.fieldName = value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setDouble(Object object, double value) throws IllegalAccessException, + IllegalArgumentException { + setDField(object, declaringClass, type, slot, flag, TYPE_DOUBLE, value); + } + + /** + * Sets the value of the field in the specified object to the {@code float} + * value. This reproduces the effect of {@code object.fieldName = value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setFloat(Object object, float value) throws IllegalAccessException, + IllegalArgumentException { + setFField(object, declaringClass, type, slot, flag, TYPE_FLOAT, value); + } + + /** + * Set the value of the field in the specified object to the {@code int} + * value. This reproduces the effect of {@code object.fieldName = value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setInt(Object object, int value) throws IllegalAccessException, + IllegalArgumentException { + setIField(object, declaringClass, type, slot, flag, TYPE_INTEGER, value); + } + + /** + * Sets the value of the field in the specified object to the {@code long} + * value. This reproduces the effect of {@code object.fieldName = value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setLong(Object object, long value) throws IllegalAccessException, + IllegalArgumentException { + setJField(object, declaringClass, type, slot, flag, TYPE_LONG, value); + } + + /** + * Sets the value of the field in the specified object to the {@code short} + * value. This reproduces the effect of {@code object.fieldName = value} + * <p> + * If this field is static, the object argument is ignored. + * Otherwise, if the object is {@code null}, a NullPointerException is + * thrown. If the object is not an instance of the declaring class of the + * method, an IllegalArgumentException is thrown. + * <p> + * If this Field object is enforcing access control (see AccessibleObject) + * and this field is not accessible from the current context, an + * IllegalAccessException is thrown. + * <p> + * If the value cannot be converted to the field type via a widening + * conversion, an IllegalArgumentException is thrown. + * + * @param object + * the object to access + * @param value + * the new value + * @throws NullPointerException + * if the object is {@code null} and the field is non-static + * @throws IllegalArgumentException + * if the object is not compatible with the declaring class + * @throws IllegalAccessException + * if this field is not accessible + */ + public void setShort(Object object, short value) throws IllegalAccessException, + IllegalArgumentException { + setSField(object, declaringClass, type, slot, flag, TYPE_SHORT, value); + } + + /** + * Returns a string containing a concise, human-readable description of this + * field. + * <p> + * The format of the string is: + * <ol> + * <li>modifiers (if any) + * <li>type + * <li>declaring class name + * <li>'.' + * <li>field name + * </ol> + * <p> + * For example: {@code public static java.io.InputStream + * java.lang.System.in} + * + * @return a printable representation for this field + */ + @Override + public String toString() { + StringBuilder result = new StringBuilder(Modifier.toString(getModifiers())); + if (result.length() != 0) { + result.append(' '); + } + appendTypeName(result, type); + result.append(' '); + appendTypeName(result, declaringClass); + result.append('.'); + result.append(name); + return result.toString(); + } + + private native Object getField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck) throws IllegalAccessException; + + private native double getDField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native int getIField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native long getJField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native boolean getZField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native float getFField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native char getCField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native short getSField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native byte getBField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor) throws IllegalAccessException; + + private native void setField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, Object value) throws IllegalAccessException; + + private native void setDField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, double v) throws IllegalAccessException; + + private native void setIField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, int i) throws IllegalAccessException; + + private native void setJField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, long j) throws IllegalAccessException; + + private native void setZField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, boolean z) throws IllegalAccessException; + + private native void setFField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, float f) throws IllegalAccessException; + + private native void setCField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, char c) throws IllegalAccessException; + + private native void setSField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, short s) throws IllegalAccessException; + + private native void setBField(Object o, Class<?> declaringClass, Class<?> type, int slot, + boolean noAccessCheck, char descriptor, byte b) throws IllegalAccessException; + +} diff --git a/libdvm/src/main/java/java/lang/reflect/Method.java b/libdvm/src/main/java/java/lang/reflect/Method.java new file mode 100644 index 0000000..f8efbf4 --- /dev/null +++ b/libdvm/src/main/java/java/lang/reflect/Method.java @@ -0,0 +1,587 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Comparator; +import libcore.util.EmptyArray; +import org.apache.harmony.kernel.vm.StringUtils; +import libcore.reflect.GenericSignatureParser; +import libcore.reflect.ListOfTypes; +import libcore.reflect.Types; + +/** + * This class represents a method. Information about the method can be accessed, + * and the method can be invoked dynamically. + */ +public final class Method extends AccessibleObject implements GenericDeclaration, Member { + + /** + * Orders methods by their name, parameters and return type. + * + * @hide + */ + public static final Comparator<Method> ORDER_BY_SIGNATURE = new Comparator<Method>() { + public int compare(Method a, Method b) { + int comparison = a.name.compareTo(b.name); + if (comparison != 0) { + return comparison; + } + + Class<?>[] aParameters = a.parameterTypes; + Class<?>[] bParameters = b.parameterTypes; + int length = Math.min(aParameters.length, bParameters.length); + for (int i = 0; i < length; i++) { + comparison = aParameters[i].getName().compareTo(bParameters[i].getName()); + if (comparison != 0) { + return comparison; + } + } + + if (aParameters.length != bParameters.length) { + return aParameters.length - bParameters.length; + } + + // this is necessary for methods that have covariant return types. + return a.getReturnType().getName().compareTo(b.getReturnType().getName()); + } + }; + + private int slot; + + private final int methodDexIndex; + + private Class<?> declaringClass; + + private String name; + + private Class<?>[] parameterTypes; + + private Class<?>[] exceptionTypes; + + private Class<?> returnType; + + private ListOfTypes genericExceptionTypes; + private ListOfTypes genericParameterTypes; + private Type genericReturnType; + private TypeVariable<Method>[] formalTypeParameters; + private volatile boolean genericTypesAreInitialized = false; + + private synchronized void initGenericTypes() { + if (!genericTypesAreInitialized) { + String signatureAttribute = getSignatureAttribute(); + GenericSignatureParser parser = new GenericSignatureParser( + declaringClass.getClassLoader()); + parser.parseForMethod(this, signatureAttribute, exceptionTypes); + formalTypeParameters = parser.formalTypeParameters; + genericParameterTypes = parser.parameterTypes; + genericExceptionTypes = parser.exceptionTypes; + genericReturnType = parser.returnType; + genericTypesAreInitialized = true; + } + } + + private Method(Class<?> declaring, Class<?>[] paramTypes, Class<?>[] exceptTypes, Class<?> returnType, String name, int slot, int methodDexIndex) { + this.declaringClass = declaring; + this.name = name; + this.slot = slot; + this.parameterTypes = paramTypes; + this.exceptionTypes = exceptTypes; // may be null + this.returnType = returnType; + this.methodDexIndex = methodDexIndex; + } + + /** @hide */ + public int getDexMethodIndex() { + return methodDexIndex; + } + + public TypeVariable<Method>[] getTypeParameters() { + initGenericTypes(); + return formalTypeParameters.clone(); + } + + /** {@inheritDoc} */ + @Override /*package*/ String getSignatureAttribute() { + Object[] annotation = getSignatureAnnotation(declaringClass, slot); + + if (annotation == null) { + return null; + } + + return StringUtils.combineStrings(annotation); + } + + /** + * Returns the Signature annotation for this method. Returns {@code null} if + * not found. + */ + static native Object[] getSignatureAnnotation(Class declaringClass, int slot); + + /** + * Returns the string representation of the method's declaration, including + * the type parameters. + * + * @return the string representation of this method + */ + public String toGenericString() { + StringBuilder sb = new StringBuilder(80); + + initGenericTypes(); + + // append modifiers if any + int modifier = getModifiers(); + if (modifier != 0) { + sb.append(Modifier.toString(modifier & ~(Modifier.BRIDGE + + Modifier.VARARGS))).append(' '); + } + // append type parameters + if (formalTypeParameters != null && formalTypeParameters.length > 0) { + sb.append('<'); + for (int i = 0; i < formalTypeParameters.length; i++) { + appendGenericType(sb, formalTypeParameters[i]); + if (i < formalTypeParameters.length - 1) { + sb.append(","); + } + } + sb.append("> "); + } + // append return type + appendGenericType(sb, Types.getType(genericReturnType)); + sb.append(' '); + // append method name + appendTypeName(sb, getDeclaringClass()); + sb.append(".").append(getName()); + // append parameters + sb.append('('); + appendArrayGenericType(sb, Types.getTypeArray(genericParameterTypes, false)); + sb.append(')'); + // append exceptions if any + Type[] genericExceptionTypeArray = Types.getTypeArray(genericExceptionTypes, false); + if (genericExceptionTypeArray.length > 0) { + sb.append(" throws "); + appendArrayGenericType(sb, genericExceptionTypeArray); + } + return sb.toString(); + } + + /** + * Returns the parameter types as an array of {@code Type} instances, in + * declaration order. If this method has no parameters, an empty array is + * returned. + * + * @return the parameter types + * + * @throws GenericSignatureFormatError + * if the generic method signature is invalid + * @throws TypeNotPresentException + * if any parameter type points to a missing type + * @throws MalformedParameterizedTypeException + * if any parameter type points to a type that cannot be + * instantiated for some reason + */ + public Type[] getGenericParameterTypes() { + initGenericTypes(); + return Types.getTypeArray(genericParameterTypes, true); + } + + /** + * Returns the exception types as an array of {@code Type} instances. If + * this method has no declared exceptions, an empty array will be returned. + * + * @return an array of generic exception types + * + * @throws GenericSignatureFormatError + * if the generic method signature is invalid + * @throws TypeNotPresentException + * if any exception type points to a missing type + * @throws MalformedParameterizedTypeException + * if any exception type points to a type that cannot be + * instantiated for some reason + */ + public Type[] getGenericExceptionTypes() { + initGenericTypes(); + return Types.getTypeArray(genericExceptionTypes, true); + } + + /** + * Returns the return type of this method as a {@code Type} instance. + * + * @return the return type of this method + * + * @throws GenericSignatureFormatError + * if the generic method signature is invalid + * @throws TypeNotPresentException + * if the return type points to a missing type + * @throws MalformedParameterizedTypeException + * if the return type points to a type that cannot be + * instantiated for some reason + */ + public Type getGenericReturnType() { + initGenericTypes(); + return Types.getType(genericReturnType); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return getDeclaredAnnotations(declaringClass, slot); + } + static native Annotation[] getDeclaredAnnotations(Class<?> declaringClass, int slot); + + @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + return getAnnotation(declaringClass, slot, annotationType); + } + static native <A extends Annotation> A getAnnotation( + Class<?> declaringClass, int slot, Class<A> annotationType); + + @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + return isAnnotationPresent(declaringClass, slot, annotationType); + } + static native boolean isAnnotationPresent( + Class<?> declaringClass, int slot, Class<? extends Annotation> annotationType); + + private static final Annotation[] NO_ANNOTATIONS = new Annotation[0]; + + /** + * Creates an array of empty Annotation arrays. + */ + /*package*/ static Annotation[][] noAnnotations(int size) { + Annotation[][] annotations = new Annotation[size][]; + for (int i = 0; i < size; i++) { + annotations[i] = NO_ANNOTATIONS; + } + return annotations; + } + + /** + * Returns an array of arrays that represent the annotations of the formal + * parameters of this method. If there are no parameters on this method, + * then an empty array is returned. If there are no annotations set, then + * and array of empty arrays is returned. + * + * @return an array of arrays of {@code Annotation} instances + */ + public Annotation[][] getParameterAnnotations() { + Annotation[][] parameterAnnotations + = getParameterAnnotations(declaringClass, slot); + if (parameterAnnotations.length == 0) { + return noAnnotations(parameterTypes.length); + } + return parameterAnnotations; + } + + static native Annotation[][] getParameterAnnotations(Class declaringClass, int slot); + + /** + * Indicates whether or not this method takes a variable number argument. + * + * @return {@code true} if a vararg is declared, {@code false} otherwise + */ + public boolean isVarArgs() { + int modifiers = getMethodModifiers(declaringClass, slot); + return (modifiers & Modifier.VARARGS) != 0; + } + + /** + * Indicates whether or not this method is a bridge. + * + * @return {@code true} if this method is a bridge, {@code false} otherwise + */ + public boolean isBridge() { + int modifiers = getMethodModifiers(declaringClass, slot); + return (modifiers & Modifier.BRIDGE) != 0; + } + + /** + * Indicates whether or not this method is synthetic. + * + * @return {@code true} if this method is synthetic, {@code false} otherwise + */ + public boolean isSynthetic() { + int modifiers = getMethodModifiers(declaringClass, slot); + return (modifiers & Modifier.SYNTHETIC) != 0; + } + + /** + * Returns the default value for the annotation member represented by this + * method. + * + * @return the default value, or {@code null} if none + * + * @throws TypeNotPresentException + * if this annotation member is of type {@code Class} and no + * definition can be found + */ + public Object getDefaultValue() { + return getDefaultValue(declaringClass, slot); + } + native private Object getDefaultValue(Class declaringClass, int slot); + + /** + * Indicates whether or not the specified {@code object} is equal to this + * method. To be equal, the specified object must be an instance + * of {@code Method} with the same declaring class and parameter types + * as this method. + * + * @param object + * the object to compare + * + * @return {@code true} if the specified object is equal to this + * method, {@code false} otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof Method)) { + return false; + } + Method rhs = (Method) object; + // We don't compare exceptionTypes because two methods + // can't differ only by their declared exceptions. + return declaringClass.equals(rhs.declaringClass) && + name.equals(rhs.name) && + getModifiers() == rhs.getModifiers() && + returnType.equals(rhs.returnType) && + Arrays.equals(parameterTypes, rhs.parameterTypes); + } + + /** + * Returns the class that declares this method. + * + * @return the declaring class + */ + public Class<?> getDeclaringClass() { + return declaringClass; + } + + /** + * Returns the exception types as an array of {@code Class} instances. If + * this method has no declared exceptions, an empty array is returned. + * + * @return the declared exception classes + */ + public Class<?>[] getExceptionTypes() { + if (exceptionTypes == null) { + return EmptyArray.CLASS; + } + return exceptionTypes.clone(); + } + + /** + * Returns the modifiers for this method. The {@link Modifier} class should + * be used to decode the result. + * + * @return the modifiers for this method + * + * @see Modifier + */ + public int getModifiers() { + return getMethodModifiers(declaringClass, slot); + } + + static native int getMethodModifiers(Class<?> declaringClass, int slot); + + /** + * Returns the name of the method represented by this {@code Method} + * instance. + * + * @return the name of this method + */ + public String getName() { + return name; + } + + /** + * Returns an array of {@code Class} objects associated with the parameter + * types of this method. If the method was declared with no parameters, an + * empty array will be returned. + * + * @return the parameter types + */ + public Class<?>[] getParameterTypes() { + return parameterTypes.clone(); + } + + /** + * Returns the {@code Class} associated with the return type of this + * method. + * + * @return the return type + */ + public Class<?> getReturnType() { + return returnType; + } + + /** + * Returns an integer hash code for this method. Objects which are equal + * return the same value for this method. The hash code for this Method is + * the hash code of the name of this method. + * + * @return hash code for this method + * + * @see #equals + */ + @Override + public int hashCode() { + return name.hashCode(); + } + + /** + * Returns the result of dynamically invoking this method. Equivalent to + * {@code receiver.methodName(arg1, arg2, ... , argN)}. + * + * <p>If the method is static, the receiver argument is ignored (and may be null). + * + * <p>If the method takes no arguments, you can pass {@code (Object[]) null} instead of + * allocating an empty array. + * + * <p>If you're calling a varargs method, you need to pass an {@code Object[]} for the + * varargs parameter: that conversion is usually done in {@code javac}, not the VM, and + * the reflection machinery does not do this for you. (It couldn't, because it would be + * ambiguous.) + * + * <p>Reflective method invocation follows the usual process for method lookup. + * + * <p>If an exception is thrown during the invocation it is caught and + * wrapped in an InvocationTargetException. This exception is then thrown. + * + * <p>If the invocation completes normally, the return value itself is + * returned. If the method is declared to return a primitive type, the + * return value is boxed. If the return type is void, null is returned. + * + * @param receiver + * the object on which to call this method (or null for static methods) + * @param args + * the arguments to the method + * @return the result + * + * @throws NullPointerException + * if {@code receiver == null} for a non-static method + * @throws IllegalAccessException + * if this method is not accessible (see {@link AccessibleObject}) + * @throws IllegalArgumentException + * if the number of arguments doesn't match the number of parameters, the receiver + * is incompatible with the declaring class, or an argument could not be unboxed + * or converted by a widening conversion to the corresponding parameter type + * @throws InvocationTargetException + * if an exception was thrown by the invoked method + */ + public Object invoke(Object receiver, Object... args) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + if (args == null) { + args = EmptyArray.OBJECT; + } + return invokeNative(receiver, args, declaringClass, parameterTypes, returnType, slot, flag); + } + + private native Object invokeNative(Object obj, Object[] args, Class<?> declaringClass, + Class<?>[] parameterTypes, Class<?> returnType, int slot, boolean noAccessCheck) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException; + + /** + * Returns a string containing a concise, human-readable description of this + * method. The format of the string is: + * + * <ol> + * <li>modifiers (if any) + * <li>return type or 'void' + * <li>declaring class name + * <li>'(' + * <li>parameter types, separated by ',' (if any) + * <li>')' + * <li>'throws' plus exception types, separated by ',' (if any) + * </ol> + * + * For example: {@code public native Object + * java.lang.Method.invoke(Object,Object) throws + * IllegalAccessException,IllegalArgumentException + * ,InvocationTargetException} + * + * @return a printable representation for this method + */ + @Override + public String toString() { + StringBuilder result = new StringBuilder(Modifier.toString(getModifiers())); + + if (result.length() != 0) + result.append(' '); + result.append(returnType.getName()); + result.append(' '); + result.append(declaringClass.getName()); + result.append('.'); + result.append(name); + result.append("("); + result.append(toString(parameterTypes)); + result.append(")"); + if (exceptionTypes != null && exceptionTypes.length != 0) { + result.append(" throws "); + result.append(toString(exceptionTypes)); + } + + return result.toString(); + } + + /** + * Returns the constructor's signature in non-printable form. This is called + * (only) from IO native code and needed for deriving the serialVersionUID + * of the class + * + * @return The constructor's signature. + */ + @SuppressWarnings("unused") + private String getSignature() { + StringBuilder result = new StringBuilder(); + + result.append('('); + for (int i = 0; i < parameterTypes.length; i++) { + result.append(getSignature(parameterTypes[i])); + } + result.append(')'); + result.append(getSignature(returnType)); + + return result.toString(); + } + +} diff --git a/libdvm/src/main/java/java/lang/reflect/Proxy.java b/libdvm/src/main/java/java/lang/reflect/Proxy.java new file mode 100644 index 0000000..3b10887 --- /dev/null +++ b/libdvm/src/main/java/java/lang/reflect/Proxy.java @@ -0,0 +1,278 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang.reflect; + +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * {@code Proxy} defines methods for creating dynamic proxy classes and instances. + * A proxy class implements a declared set of interfaces and delegates method + * invocations to an {@code InvocationHandler}. + * + * @see InvocationHandler + * @since 1.3 + */ +public class Proxy implements Serializable { + + private static final long serialVersionUID = -2222568056686623797L; + + // maps class loaders to created classes by interface names + private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> loaderCache = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>(); + + // to find previously created types + private static final Map<Class<?>, String> proxyCache = new WeakHashMap<Class<?>, String>(); + + private static int NextClassNameIndex = 0; + + /** + * The invocation handler on which the method calls are dispatched. + */ + protected InvocationHandler h; + + @SuppressWarnings("unused") + private Proxy() { + } + + /** + * Constructs a new {@code Proxy} instance with the specified invocation + * handler. + * + * @param h + * the invocation handler for the newly created proxy + */ + protected Proxy(InvocationHandler h) { + this.h = h; + } + + /** + * Returns the dynamically built {@code Class} for the specified interfaces. + * Creates a new {@code Class} when necessary. The order of the interfaces + * is relevant. Invocations of this method with the same interfaces but + * different order result in different generated classes. The interfaces + * must be visible from the supplied class loader; no duplicates are + * permitted. All non-public interfaces must be defined in the same package. + * + * @param loader + * the class loader that will define the proxy class + * @param interfaces + * an array of {@code Class} objects, each one identifying an + * interface that will be implemented by the returned proxy + * class + * @return a proxy class that implements all of the interfaces referred to + * in the contents of {@code interfaces} + * @throws IllegalArgumentException + * if any of the interface restrictions are violated + * @throws NullPointerException + * if either {@code interfaces} or any of its elements are + * {@code null} + */ + public static Class<?> getProxyClass(ClassLoader loader, + Class<?>... interfaces) throws IllegalArgumentException { + // check that interfaces are a valid array of visible interfaces + if (interfaces == null) { + throw new NullPointerException("interfaces == null"); + } + String commonPackageName = null; + for (int i = 0, length = interfaces.length; i < length; i++) { + Class<?> next = interfaces[i]; + if (next == null) { + throw new NullPointerException("interfaces[" + i + "] == null"); + } + String name = next.getName(); + if (!next.isInterface()) { + throw new IllegalArgumentException(name + " is not an interface"); + } + if (loader != next.getClassLoader()) { + try { + if (next != Class.forName(name, false, loader)) { + throw new IllegalArgumentException(name + + " is not visible from class loader"); + } + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException(name + " is not visible from class loader"); + } + } + for (int j = i + 1; j < length; j++) { + if (next == interfaces[j]) { + throw new IllegalArgumentException(name + " appears more than once"); + } + } + if (!Modifier.isPublic(next.getModifiers())) { + int last = name.lastIndexOf('.'); + String p = last == -1 ? "" : name.substring(0, last); + if (commonPackageName == null) { + commonPackageName = p; + } else if (!commonPackageName.equals(p)) { + throw new IllegalArgumentException("non-public interfaces must be " + + "in the same package"); + } + } + } + + // search cache for matching proxy class using the class loader + synchronized (loaderCache) { + Map<String, WeakReference<Class<?>>> interfaceCache = loaderCache + .get(loader); + if (interfaceCache == null) { + loaderCache + .put( + loader, + (interfaceCache = new HashMap<String, WeakReference<Class<?>>>())); + } + + String interfaceKey = ""; + if (interfaces.length == 1) { + interfaceKey = interfaces[0].getName(); + } else { + StringBuilder names = new StringBuilder(); + for (int i = 0, length = interfaces.length; i < length; i++) { + names.append(interfaces[i].getName()); + names.append(' '); + } + interfaceKey = names.toString(); + } + + Class<?> newClass; + WeakReference<Class<?>> ref = interfaceCache.get(interfaceKey); + if (ref == null) { + String nextClassName = "$Proxy" + NextClassNameIndex++; + if (commonPackageName != null && commonPackageName.length() > 0) { + nextClassName = commonPackageName + "." + nextClassName; + } + if (loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + newClass = generateProxy(nextClassName.replace('.', '/'), interfaces, loader); + // Need a weak reference to the class so it can + // be unloaded if the class loader is discarded + interfaceCache.put(interfaceKey, new WeakReference<Class<?>>(newClass)); + synchronized (proxyCache) { + // the value is unused + proxyCache.put(newClass, ""); + } + } else { + newClass = ref.get(); + assert newClass != null : "\ninterfaceKey=\"" + interfaceKey + "\"" + + "\nloaderCache=\"" + loaderCache + "\"" + + "\nintfCache=\"" + interfaceCache + "\"" + + "\nproxyCache=\"" + proxyCache + "\""; + } + return newClass; + } + } + + /** + * Returns an instance of the dynamically built class for the specified + * interfaces. Method invocations on the returned instance are forwarded to + * the specified invocation handler. The interfaces must be visible from the + * supplied class loader; no duplicates are permitted. All non-public + * interfaces must be defined in the same package. + * + * @param loader + * the class loader that will define the proxy class + * @param interfaces + * an array of {@code Class} objects, each one identifying an + * interface that will be implemented by the returned proxy + * object + * @param h + * the invocation handler that handles the dispatched method + * invocations + * @return a new proxy object that delegates to the handler {@code h} + * @throws IllegalArgumentException + * if any of the interface restrictions are violated + * @throws NullPointerException + * if the interfaces or any of its elements are null + */ + public static Object newProxyInstance(ClassLoader loader, + Class<?>[] interfaces, InvocationHandler h) + throws IllegalArgumentException { + if (h == null) { + throw new NullPointerException("h == null"); + } + try { + return getProxyClass(loader, interfaces).getConstructor( + new Class<?>[] { InvocationHandler.class }).newInstance( + new Object[] { h }); + } catch (NoSuchMethodException ex) { + throw (InternalError) (new InternalError(ex.toString()) + .initCause(ex)); + } catch (IllegalAccessException ex) { + throw (InternalError) (new InternalError(ex.toString()) + .initCause(ex)); + } catch (InstantiationException ex) { + throw (InternalError) (new InternalError(ex.toString()) + .initCause(ex)); + } catch (InvocationTargetException ex) { + Throwable target = ex.getTargetException(); + throw (InternalError) (new InternalError(target.toString()) + .initCause(target)); + } + } + + /** + * Indicates whether or not the specified class is a dynamically generated + * proxy class. + * + * @param cl + * the class + * @return {@code true} if the class is a proxy class, {@code false} + * otherwise + * @throws NullPointerException + * if the class is {@code null} + */ + public static boolean isProxyClass(Class<?> cl) { + if (cl == null) { + throw new NullPointerException("cl == null"); + } + synchronized (proxyCache) { + return proxyCache.containsKey(cl); + } + } + + /** + * Returns the invocation handler of the specified proxy instance. + * + * @param proxy + * the proxy instance + * @return the invocation handler of the specified proxy instance + * @throws IllegalArgumentException + * if the supplied {@code proxy} is not a proxy object + */ + public static InvocationHandler getInvocationHandler(Object proxy) + throws IllegalArgumentException { + + if (isProxyClass(proxy.getClass())) { + return ((Proxy) proxy).h; + } + + throw new IllegalArgumentException("not a proxy instance"); + } + + native private static Class generateProxy(String name, Class[] interfaces, + ClassLoader loader); + + /* + * The VM clones this method's descriptor when generating a proxy class. + * There is no implementation. + */ + native private static void constructorPrototype(InvocationHandler h); +} diff --git a/libdvm/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java b/libdvm/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java new file mode 100644 index 0000000..01d0e30 --- /dev/null +++ b/libdvm/src/main/java/org/apache/harmony/kernel/vm/StringUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.kernel.vm; + +/** + * String utility functions. + */ +public final class StringUtils { + /** + * This class is uninstantiable. + */ + private StringUtils() { + // This space intentionally left blank. + } + + /** + * Combine a list of strings in an <code>Object[]</code> into a single + * string. + * + * @param list non-null; the strings to combine + * @return non-null; the combined form + */ + public static String combineStrings(Object[] list) { + int listLength = list.length; + + switch (listLength) { + case 0: { + return ""; + } + case 1: { + return (String) list[0]; + } + } + + int strLength = 0; + + for (int i = 0; i < listLength; i++) { + strLength += ((String) list[i]).length(); + } + + StringBuilder sb = new StringBuilder(strLength); + + for (int i = 0; i < listLength; i++) { + sb.append(list[i]); + } + + return sb.toString(); + } +} diff --git a/libdvm/src/main/java/sun/misc/Unsafe.java b/libdvm/src/main/java/sun/misc/Unsafe.java new file mode 100644 index 0000000..884340b --- /dev/null +++ b/libdvm/src/main/java/sun/misc/Unsafe.java @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sun.misc; + +import dalvik.system.VMStack; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +/** + * The package name notwithstanding, this class is the quasi-standard + * way for Java code to gain access to and use functionality which, + * when unsupervised, would allow one to break the pointer/type safety + * of Java. + */ +public final class Unsafe { + /** Traditional dalvik name. */ + private static final Unsafe THE_ONE = new Unsafe(); + /** Traditional RI name. */ + private static final Unsafe theUnsafe = THE_ONE; + + /** + * This class is only privately instantiable. + */ + private Unsafe() {} + + /** + * Gets the unique instance of this class. This is only allowed in + * very limited situations. + */ + public static Unsafe getUnsafe() { + /* + * Only code on the bootclasspath is allowed to get at the + * Unsafe instance. + */ + ClassLoader calling = VMStack.getCallingClassLoader(); + if ((calling != null) && (calling != Unsafe.class.getClassLoader())) { + throw new SecurityException("Unsafe access denied"); + } + + return THE_ONE; + } + + /** + * Gets the raw byte offset from the start of an object's memory to + * the memory used to store the indicated instance field. + * + * @param field non-null; the field in question, which must be an + * instance field + * @return the offset to the field + */ + public long objectFieldOffset(Field field) { + if (Modifier.isStatic(field.getModifiers())) { + throw new IllegalArgumentException( + "valid for instance fields only"); + } + + return objectFieldOffset0(field); + } + + /** + * Helper for {@link #objectFieldOffset}, which does all the work, + * assuming the parameter is deemed valid. + * + * @param field non-null; the instance field + * @return the offset to the field + */ + private static native long objectFieldOffset0(Field field); + + /** + * Gets the offset from the start of an array object's memory to + * the memory used to store its initial (zeroeth) element. + * + * @param clazz non-null; class in question; must be an array class + * @return the offset to the initial element + */ + public int arrayBaseOffset(Class clazz) { + if (! clazz.isArray()) { + throw new IllegalArgumentException( + "valid for array classes only"); + } + + return arrayBaseOffset0(clazz); + } + + /** + * Helper for {@link #arrayBaseOffset}, which does all the work, + * assuming the parameter is deemed valid. + * + * @return the offset to the field + */ + private static native int arrayBaseOffset0(Class clazz); + + /** + * Gets the size of each element of the given array class. + * + * @param clazz non-null; class in question; must be an array class + * @return > 0; the size of each element of the array + */ + public int arrayIndexScale(Class clazz) { + if (! clazz.isArray()) { + throw new IllegalArgumentException( + "valid for array classes only"); + } + + return arrayIndexScale0(clazz); + } + + /** + * Helper for {@link #arrayIndexScale}, which does all the work, + * assuming the parameter is deemed valid. + * + * @return the offset to the field + */ + private static native int arrayIndexScale0(Class clazz); + + /** + * Performs a compare-and-set operation on an <code>int</code> + * field within the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param expectedValue expected value of the field + * @param newValue new value to store in the field if the contents are + * as expected + * @return <code>true</code> if the new value was in fact stored, and + * <code>false</code> if not + */ + public native boolean compareAndSwapInt(Object obj, long offset, + int expectedValue, int newValue); + + /** + * Performs a compare-and-set operation on a <code>long</code> + * field within the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param expectedValue expected value of the field + * @param newValue new value to store in the field if the contents are + * as expected + * @return <code>true</code> if the new value was in fact stored, and + * <code>false</code> if not + */ + public native boolean compareAndSwapLong(Object obj, long offset, + long expectedValue, long newValue); + + /** + * Performs a compare-and-set operation on an <code>Object</code> + * field (that is, a reference field) within the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param expectedValue expected value of the field + * @param newValue new value to store in the field if the contents are + * as expected + * @return <code>true</code> if the new value was in fact stored, and + * <code>false</code> if not + */ + public native boolean compareAndSwapObject(Object obj, long offset, + Object expectedValue, Object newValue); + + /** + * Gets an <code>int</code> field from the given object, + * using <code>volatile</code> semantics. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @return the retrieved value + */ + public native int getIntVolatile(Object obj, long offset); + + /** + * Stores an <code>int</code> field into the given object, + * using <code>volatile</code> semantics. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param newValue the value to store + */ + public native void putIntVolatile(Object obj, long offset, int newValue); + + /** + * Gets a <code>long</code> field from the given object, + * using <code>volatile</code> semantics. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @return the retrieved value + */ + public native long getLongVolatile(Object obj, long offset); + + /** + * Stores a <code>long</code> field into the given object, + * using <code>volatile</code> semantics. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param newValue the value to store + */ + public native void putLongVolatile(Object obj, long offset, long newValue); + + /** + * Gets an <code>Object</code> field from the given object, + * using <code>volatile</code> semantics. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @return the retrieved value + */ + public native Object getObjectVolatile(Object obj, long offset); + + /** + * Stores an <code>Object</code> field into the given object, + * using <code>volatile</code> semantics. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param newValue the value to store + */ + public native void putObjectVolatile(Object obj, long offset, + Object newValue); + + /** + * Gets an <code>int</code> field from the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @return the retrieved value + */ + public native int getInt(Object obj, long offset); + + /** + * Stores an <code>int</code> field into the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param newValue the value to store + */ + public native void putInt(Object obj, long offset, int newValue); + + /** + * Lazy set an int field. + */ + public native void putOrderedInt(Object obj, long offset, int newValue); + + /** + * Gets a <code>long</code> field from the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @return the retrieved value + */ + public native long getLong(Object obj, long offset); + + /** + * Stores a <code>long</code> field into the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param newValue the value to store + */ + public native void putLong(Object obj, long offset, long newValue); + + /** + * Lazy set a long field. + */ + public native void putOrderedLong(Object obj, long offset, long newValue); + + /** + * Gets an <code>Object</code> field from the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @return the retrieved value + */ + public native Object getObject(Object obj, long offset); + + /** + * Stores an <code>Object</code> field into the given object. + * + * @param obj non-null; object containing the field + * @param offset offset to the field within <code>obj</code> + * @param newValue the value to store + */ + public native void putObject(Object obj, long offset, Object newValue); + + /** + * Lazy set an object field. + */ + public native void putOrderedObject(Object obj, long offset, + Object newValue); + + /** + * Parks the calling thread for the specified amount of time, + * unless the "permit" for the thread is already available (due to + * a previous call to {@link #unpark}. This method may also return + * spuriously (that is, without the thread being told to unpark + * and without the indicated amount of time elapsing). + * + * <p>See {@link java.util.concurrent.locks.LockSupport} for more + * in-depth information of the behavior of this method.</p> + * + * @param absolute whether the given time value is absolute + * milliseconds-since-the-epoch (<code>true</code>) or relative + * nanoseconds-from-now (<code>false</code>) + * @param time the (absolute millis or relative nanos) time value + */ + public void park(boolean absolute, long time) { + if (absolute) { + Thread.currentThread().parkUntil(time); + } else { + Thread.currentThread().parkFor(time); + } + } + + /** + * Unparks the given object, which must be a {@link Thread}. + * + * <p>See {@link java.util.concurrent.locks.LockSupport} for more + * in-depth information of the behavior of this method.</p> + * + * @param obj non-null; the object to unpark + */ + public void unpark(Object obj) { + if (obj instanceof Thread) { + ((Thread) obj).unpark(); + } else { + throw new IllegalArgumentException("valid for Threads only"); + } + } + + /** + * Allocates an instance of the given class without running the constructor. + * The class' <clinit> will be run, if necessary. + */ + public native Object allocateInstance(Class<?> c); +} diff --git a/luni/src/main/java/android/system/ErrnoException.java b/luni/src/main/java/android/system/ErrnoException.java index 134d6a0..90155c8 100644 --- a/luni/src/main/java/android/system/ErrnoException.java +++ b/luni/src/main/java/android/system/ErrnoException.java @@ -24,8 +24,6 @@ import libcore.io.Libcore; * A checked exception thrown when {@link Os} methods fail. This exception contains the native * errno value, for comparison against the constants in {@link OsConstants}, should sophisticated * callers need to adjust their behavior based on the exact failure. - * - * @hide */ public final class ErrnoException extends Exception { private final String functionName; diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java index e7613df..0b80b52 100644 --- a/luni/src/main/java/android/system/Os.java +++ b/luni/src/main/java/android/system/Os.java @@ -47,8 +47,6 @@ import libcore.io.Libcore; * primitives used to implement the higher-level APIs. * * <p>The corresponding constants can be found in {@link OsConstants}. - * - * @hide */ public final class Os { private Os() {} @@ -63,6 +61,8 @@ public final class Os { */ public static boolean access(String path, int mode) throws ErrnoException { return Libcore.os.access(path, mode); } + /** @hide */ public static InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return Libcore.os.android_getaddrinfo(node, hints, netId); } + /** * See <a href="http://man7.org/linux/man-pages/man2/bind.2.html">bind(2)</a>. */ @@ -157,8 +157,6 @@ public final class Os { */ public static String gai_strerror(int error) { return Libcore.os.gai_strerror(error); } - /** @hide */ public static InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException { return Libcore.os.getaddrinfo(node, hints); } - /** * See <a href="http://man7.org/linux/man-pages/man2/getegid.2.html">getegid(2)</a>. */ diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java index 0a49311..c758eb7 100644 --- a/luni/src/main/java/android/system/OsConstants.java +++ b/luni/src/main/java/android/system/OsConstants.java @@ -18,7 +18,6 @@ package android.system; /** * Constants and helper functions for use with {@link Os}. - * @hide */ public final class OsConstants { private OsConstants() { diff --git a/luni/src/main/java/android/system/StructPollfd.java b/luni/src/main/java/android/system/StructPollfd.java index 8bdecb2..b812612 100644 --- a/luni/src/main/java/android/system/StructPollfd.java +++ b/luni/src/main/java/android/system/StructPollfd.java @@ -22,8 +22,6 @@ import libcore.util.Objects; /** * Used as an in/out parameter to {@link Os#poll}. * Corresponds to C's {@code struct pollfd} from {@code <poll.h>}. - * - * @hide */ public final class StructPollfd { /** The file descriptor to poll. */ diff --git a/luni/src/main/java/android/system/StructStat.java b/luni/src/main/java/android/system/StructStat.java index 87bd50c..a6958c1 100644 --- a/luni/src/main/java/android/system/StructStat.java +++ b/luni/src/main/java/android/system/StructStat.java @@ -21,8 +21,6 @@ import libcore.util.Objects; /** * File information returned by {@link Os#fstat}, {@link Os#lstat}, and {@link Os#stat}. * Corresponds to C's {@code struct stat} from {@code <stat.h>}. - * - * @hide */ public final class StructStat { /** Device ID of device containing file. */ diff --git a/luni/src/main/java/android/system/StructStatVfs.java b/luni/src/main/java/android/system/StructStatVfs.java index b0b7802..942a39a 100644 --- a/luni/src/main/java/android/system/StructStatVfs.java +++ b/luni/src/main/java/android/system/StructStatVfs.java @@ -20,8 +20,6 @@ import libcore.util.Objects; /** * File information returned by {@link Os#fstatvfs} and {@link Os#statvfs}. - * - * @hide */ public final class StructStatVfs { /** File system block size (used for block counts). */ diff --git a/luni/src/main/java/android/system/StructUtsname.java b/luni/src/main/java/android/system/StructUtsname.java index c62dbfa..5d9127b 100644 --- a/luni/src/main/java/android/system/StructUtsname.java +++ b/luni/src/main/java/android/system/StructUtsname.java @@ -21,8 +21,6 @@ import libcore.util.Objects; /** * Information returned by {@link Os#uname}. * Corresponds to C's {@code struct utsname} from {@code <sys/utsname.h>}. - * - * @hide */ public final class StructUtsname { /** The OS name, such as "Linux". */ diff --git a/luni/src/main/java/android/util/MutableBoolean.java b/luni/src/main/java/android/util/MutableBoolean.java index 90bf68c..5a8a200 100644 --- a/luni/src/main/java/android/util/MutableBoolean.java +++ b/luni/src/main/java/android/util/MutableBoolean.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableBoolean { public boolean value; diff --git a/luni/src/main/java/android/util/MutableByte.java b/luni/src/main/java/android/util/MutableByte.java index 65738b9..7397ba4 100644 --- a/luni/src/main/java/android/util/MutableByte.java +++ b/luni/src/main/java/android/util/MutableByte.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableByte { public byte value; diff --git a/luni/src/main/java/android/util/MutableChar.java b/luni/src/main/java/android/util/MutableChar.java index b59bab3..f435331 100644 --- a/luni/src/main/java/android/util/MutableChar.java +++ b/luni/src/main/java/android/util/MutableChar.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableChar { public char value; diff --git a/luni/src/main/java/android/util/MutableDouble.java b/luni/src/main/java/android/util/MutableDouble.java index 3e2cc3a..f62f47e 100644 --- a/luni/src/main/java/android/util/MutableDouble.java +++ b/luni/src/main/java/android/util/MutableDouble.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableDouble { public double value; diff --git a/luni/src/main/java/android/util/MutableFloat.java b/luni/src/main/java/android/util/MutableFloat.java index 6e30501..6b5441c 100644 --- a/luni/src/main/java/android/util/MutableFloat.java +++ b/luni/src/main/java/android/util/MutableFloat.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableFloat { public float value; diff --git a/luni/src/main/java/android/util/MutableInt.java b/luni/src/main/java/android/util/MutableInt.java index 8220c44..2f93030 100644 --- a/luni/src/main/java/android/util/MutableInt.java +++ b/luni/src/main/java/android/util/MutableInt.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableInt { public int value; diff --git a/luni/src/main/java/android/util/MutableLong.java b/luni/src/main/java/android/util/MutableLong.java index 5df6a0d..94beab5 100644 --- a/luni/src/main/java/android/util/MutableLong.java +++ b/luni/src/main/java/android/util/MutableLong.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableLong { public long value; diff --git a/luni/src/main/java/android/util/MutableShort.java b/luni/src/main/java/android/util/MutableShort.java index 3880fef..cdd9923 100644 --- a/luni/src/main/java/android/util/MutableShort.java +++ b/luni/src/main/java/android/util/MutableShort.java @@ -17,7 +17,6 @@ package android.util; /** - * @hide */ public final class MutableShort { public short value; diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java index d07075f..cd7f736 100644 --- a/luni/src/main/java/java/io/ObjectInputStream.java +++ b/luni/src/main/java/java/io/ObjectInputStream.java @@ -1053,7 +1053,8 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec * @see #readFields * @see #readObject() */ - private void readFieldValues(Object obj, ObjectStreamClass classDesc) throws OptionalDataException, ClassNotFoundException, IOException { + private void readFieldValues(Object obj, ObjectStreamClass classDesc) + throws OptionalDataException, ClassNotFoundException, IOException { // Now we must read all fields and assign them to the receiver ObjectStreamField[] fields = classDesc.getLoadFields(); fields = (fields == null) ? ObjectStreamClass.NO_FIELDS : fields; @@ -1577,6 +1578,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec ClassNotFoundException, IOException { // read classdesc for Enum first ObjectStreamClass classDesc = readEnumDesc(); + + Class enumType = classDesc.checkAndGetTcEnumClass(); + int newHandle = nextHandle(); // read name after class desc String name; @@ -1598,9 +1602,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec Enum<?> result; try { - result = Enum.valueOf((Class) classDesc.forClass(), name); + result = Enum.valueOf(enumType, name); } catch (IllegalArgumentException e) { - throw new InvalidObjectException(e.getMessage()); + InvalidObjectException ioe = new InvalidObjectException(e.getMessage()); + ioe.initCause(e); + throw ioe; } registerObjectRead(result, newHandle, unshared); return result; @@ -1782,9 +1788,10 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec throw missingClassDescriptor(); } + Class<?> objectClass = classDesc.checkAndGetTcObjectClass(); + int newHandle = nextHandle(); - Class<?> objectClass = classDesc.forClass(); - Object result = null; + Object result; Object registeredResult = null; if (objectClass != null) { // Now we know which class to instantiate and which constructor to @@ -2056,8 +2063,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec * if the source stream does not contain readable serialized * objects. */ - protected void readStreamHeader() throws IOException, - StreamCorruptedException { + protected void readStreamHeader() throws IOException, StreamCorruptedException { if (input.readShort() == STREAM_MAGIC && input.readShort() == STREAM_VERSION) { return; @@ -2257,7 +2263,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec // not primitive class // Use the first non-null ClassLoader on the stack. If null, use // the system class loader - cls = Class.forName(className, true, callerClassLoader); + cls = Class.forName(className, false, callerClassLoader); } } return cls; @@ -2331,8 +2337,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec throws InvalidClassException { Class<?> localClass = loadedStreamClass.forClass(); - ObjectStreamClass localStreamClass = ObjectStreamClass - .lookupStreamClass(localClass); + ObjectStreamClass localStreamClass = ObjectStreamClass.lookupStreamClass(localClass); if (loadedStreamClass.getSerialVersionUID() != localStreamClass .getSerialVersionUID()) { diff --git a/luni/src/main/java/java/io/ObjectStreamClass.java b/luni/src/main/java/java/io/ObjectStreamClass.java index 1bde314..9b7f2c9 100644 --- a/luni/src/main/java/java/io/ObjectStreamClass.java +++ b/luni/src/main/java/java/io/ObjectStreamClass.java @@ -1068,7 +1068,6 @@ public class ObjectStreamClass implements Serializable { tlc.put(cl, cachedValue); } return cachedValue; - } /** @@ -1298,4 +1297,72 @@ public class ObjectStreamClass implements Serializable { public String toString() { return getName() + ": static final long serialVersionUID =" + getSerialVersionUID() + "L;"; } + + /** + * Checks the local class to make sure it is valid for {@link ObjectStreamConstants#TC_OBJECT} + * deserialization. Also performs some sanity checks of the stream data. This method is used + * during deserialization to confirm the local class is likely to be compatible with the coming + * stream data, but before an instance is instantiated. + * + * @hide used internally during deserialization + */ + public Class<?> checkAndGetTcObjectClass() throws InvalidClassException { + // We check some error possibilities that might cause problems later. + boolean wasSerializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; + boolean wasExternalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; + if (wasSerializable == wasExternalizable) { + throw new InvalidClassException( + getName() + " stream data is corrupt: SC_SERIALIZABLE=" + wasSerializable + + " SC_EXTERNALIZABLE=" + wasExternalizable + + ", classDescFlags must have one or the other"); + } + + // TC_ENUM is handled elsewhere. See checkAndGetTcEnumClass(). + if (isEnum()) { + throw new InvalidClassException( + getName() + " local class is incompatible: Local class is an enum, streamed" + + " data is tagged with TC_OBJECT"); + } + + // isSerializable() is true if the local class implements Serializable. Externalizable + // classes are also Serializable via inheritance. + if (!isSerializable()) { + throw new InvalidClassException(getName() + " local class is incompatible: Not" + + " Serializable"); + } + + // The stream class was externalizable, but is only serializable locally. + if (wasExternalizable != isExternalizable()) { + throw new InvalidClassException( + getName() + " local class is incompatible: Local class is Serializable, stream" + + " data requires Externalizable"); + } + + // The following are left unchecked and thus are treated leniently at this point. + // SC_BLOCK_DATA may be set iff SC_EXTERNALIZABLE is set AND version 2 of the protocol is in + // use. + // SC_ENUM should not be set. + + return forClass(); + } + + /** + * Checks the local class to make sure it is valid for {@link ObjectStreamConstants#TC_ENUM} + * deserialization. This method is used during deserialization to confirm the local class is + * likely to be compatible with the coming stream data, but before an instance is instantiated. + * + * @hide used internally during deserialization + */ + public Class<?> checkAndGetTcEnumClass() throws InvalidClassException { + if (!isEnum()) { + throw new InvalidClassException( + getName() + " local class is incompatible: Local class is not an enum," + + " streamed data is tagged with TC_ENUM"); + } + + // The stream flags are expected to be SC_SERIALIZABLE | SC_ENUM but these and the + // other flags are not used when reading enum data so they are treated leniently. + + return forClass(); + } } diff --git a/luni/src/main/java/java/io/ObjectStreamConstants.java b/luni/src/main/java/java/io/ObjectStreamConstants.java index 8228b33..95f8b03 100644 --- a/luni/src/main/java/java/io/ObjectStreamConstants.java +++ b/luni/src/main/java/java/io/ObjectStreamConstants.java @@ -149,25 +149,25 @@ public abstract interface ObjectStreamConstants { // Flags that indicate if the object was serializable, externalizable // and had a writeObject method when dumped. /** - * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates - * that a serializable class has its own {@code writeObject} method. + * Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates + * that a {@link Serializable} class has its own {@code writeObject} method. */ public static final byte SC_WRITE_METHOD = 0x01; // If SC_SERIALIZABLE /** - * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates - * that a class is serializable. + * Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates + * that a class implements {@link Serializable} but not {@link Externalizable}. */ public static final byte SC_SERIALIZABLE = 0x02; /** - * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates - * that a class is externalizable. + * Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates + * that a class implements {@link Externalizable}. */ public static final byte SC_EXTERNALIZABLE = 0x04; /** - * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates + * Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates * that an externalizable class is written in block data mode. */ public static final byte SC_BLOCK_DATA = 0x08; // If SC_EXTERNALIZABLE @@ -178,7 +178,7 @@ public abstract interface ObjectStreamConstants { public static final byte TC_ENUM = 0x7E; /** - * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates + * Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates * that a class is an enum type. */ public static final byte SC_ENUM = 0x10; diff --git a/luni/src/main/java/java/net/AddressCache.java b/luni/src/main/java/java/net/AddressCache.java index 194761a..2aba78b 100644 --- a/luni/src/main/java/java/net/AddressCache.java +++ b/luni/src/main/java/java/net/AddressCache.java @@ -37,8 +37,36 @@ class AddressCache { private static final long TTL_NANOS = 2 * 1000000000L; // The actual cache. - private final BasicLruCache<String, AddressCacheEntry> cache - = new BasicLruCache<String, AddressCacheEntry>(MAX_ENTRIES); + private final BasicLruCache<AddressCacheKey, AddressCacheEntry> cache + = new BasicLruCache<AddressCacheKey, AddressCacheEntry>(MAX_ENTRIES); + + static class AddressCacheKey { + private final String mHostname; + private final int mNetId; + + AddressCacheKey(String hostname, int netId) { + mHostname = hostname; + mNetId = netId; + } + + @Override public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AddressCacheKey)) { + return false; + } + AddressCacheKey lhs = (AddressCacheKey) o; + return mHostname.equals(lhs.mHostname) && mNetId == lhs.mNetId; + } + + @Override public int hashCode() { + int result = 17; + result = 31 * result + mNetId; + result = 31 * result + mHostname.hashCode(); + return result; + } + } static class AddressCacheEntry { // Either an InetAddress[] for a positive entry, @@ -67,12 +95,12 @@ class AddressCache { } /** - * Returns the cached InetAddress[] associated with 'hostname'. Returns null if nothing is known - * about 'hostname'. Returns a String suitable for use as an UnknownHostException detail - * message if 'hostname' is known not to exist. + * Returns the cached InetAddress[] for 'hostname' on network 'netId'. Returns null + * if nothing is known about 'hostname'. Returns a String suitable for use as an + * UnknownHostException detail message if 'hostname' is known not to exist. */ - public Object get(String hostname) { - AddressCacheEntry entry = cache.get(hostname); + public Object get(String hostname, int netId) { + AddressCacheEntry entry = cache.get(new AddressCacheKey(hostname, netId)); // Do we have a valid cache entry? if (entry != null && entry.expiryNanos >= System.nanoTime()) { return entry.value; @@ -86,15 +114,15 @@ class AddressCache { * Associates the given 'addresses' with 'hostname'. The association will expire after a * certain length of time. */ - public void put(String hostname, InetAddress[] addresses) { - cache.put(hostname, new AddressCacheEntry(addresses)); + public void put(String hostname, int netId, InetAddress[] addresses) { + cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(addresses)); } /** * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a * negative cache entry.) */ - public void putUnknownHost(String hostname, String detailMessage) { - cache.put(hostname, new AddressCacheEntry(detailMessage)); + public void putUnknownHost(String hostname, int netId, String detailMessage) { + cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(detailMessage)); } } diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java index e31b4c3..5cfa15a 100644 --- a/luni/src/main/java/java/net/InetAddress.java +++ b/luni/src/main/java/java/net/InetAddress.java @@ -127,6 +127,9 @@ public class InetAddress implements Serializable { private static final long serialVersionUID = 3286316764910316507L; + /** Using NetID of NETID_UNSET indicates resolution should be done on default network. */ + private static final int NETID_UNSET = 0; + private int family; byte[] ipaddress; @@ -209,14 +212,29 @@ public class InetAddress implements Serializable { * @throws UnknownHostException if the address lookup fails. */ public static InetAddress[] getAllByName(String host) throws UnknownHostException { - return getAllByNameImpl(host).clone(); + return getAllByNameImpl(host, NETID_UNSET).clone(); } /** - * Returns the InetAddresses for {@code host}. The returned array is shared - * and must be cloned before it is returned to application code. + * Operates identically to {@code getAllByName} except host resolution is + * performed on the network designated by {@code netId}. + * + * @param host the hostname or literal IP string to be resolved. + * @param netId the network to use for host resolution. + * @return the array of addresses associated with the specified host. + * @throws UnknownHostException if the address lookup fails. + * @hide internal use only */ - private static InetAddress[] getAllByNameImpl(String host) throws UnknownHostException { + public static InetAddress[] getAllByNameOnNet(String host, int netId) throws UnknownHostException { + return getAllByNameImpl(host, netId).clone(); + } + + /** + * Returns the InetAddresses for {@code host} on network {@code netId}. The + * returned array is shared and must be cloned before it is returned to + * application code. + */ + private static InetAddress[] getAllByNameImpl(String host, int netId) throws UnknownHostException { if (host == null || host.isEmpty()) { return loopbackAddresses(); } @@ -231,7 +249,7 @@ public class InetAddress implements Serializable { return new InetAddress[] { result }; } - return lookupHostByName(host).clone(); + return lookupHostByName(host, netId).clone(); } private static InetAddress makeInetAddress(byte[] bytes, String hostName) throws UnknownHostException { @@ -264,7 +282,7 @@ public class InetAddress implements Serializable { hints.ai_flags = AI_NUMERICHOST; InetAddress[] addresses = null; try { - addresses = Libcore.os.getaddrinfo(address, hints); + addresses = Libcore.os.android_getaddrinfo(address, hints, NETID_UNSET); } catch (GaiException ignored) { } return (addresses != null) ? addresses[0] : null; @@ -284,7 +302,22 @@ public class InetAddress implements Serializable { * if the address lookup fails. */ public static InetAddress getByName(String host) throws UnknownHostException { - return getAllByNameImpl(host)[0]; + return getAllByNameImpl(host, NETID_UNSET)[0]; + } + + /** + * Operates identically to {@code getByName} except host resolution is + * performed on the network designated by {@code netId}. + * + * @param host + * the hostName to be resolved to an address or {@code null}. + * @param netId the network to use for host resolution. + * @return the {@code InetAddress} instance representing the host. + * @throws UnknownHostException if the address lookup fails. + * @hide internal use only + */ + public static InetAddress getByNameOnNet(String host, int netId) throws UnknownHostException { + return getAllByNameImpl(host, netId)[0]; } /** @@ -360,7 +393,7 @@ public class InetAddress implements Serializable { */ public static InetAddress getLocalHost() throws UnknownHostException { String host = Libcore.os.uname().nodename; - return lookupHostByName(host)[0]; + return lookupHostByName(host, NETID_UNSET)[0]; } /** @@ -377,12 +410,14 @@ public class InetAddress implements Serializable { * Resolves a hostname to its IP addresses using a cache. * * @param host the hostname to resolve. + * @param netId the network to perform resolution upon. * @return the IP addresses of the host. */ - private static InetAddress[] lookupHostByName(String host) throws UnknownHostException { + private static InetAddress[] lookupHostByName(String host, int netId) + throws UnknownHostException { BlockGuard.getThreadPolicy().onNetwork(); // Do we have a result cached? - Object cachedResult = addressCache.get(host); + Object cachedResult = addressCache.get(host, netId); if (cachedResult != null) { if (cachedResult instanceof InetAddress[]) { // A cached positive result. @@ -400,12 +435,12 @@ public class InetAddress implements Serializable { // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family // anyway, just pick one. hints.ai_socktype = SOCK_STREAM; - InetAddress[] addresses = Libcore.os.getaddrinfo(host, hints); + InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId); // TODO: should getaddrinfo set the hostname of the InetAddresses it returns? for (InetAddress address : addresses) { address.hostName = host; } - addressCache.put(host, addresses); + addressCache.put(host, netId, addresses); return addresses; } catch (GaiException gaiException) { // If the failure appears to have been a lack of INTERNET permission, throw a clear @@ -418,7 +453,7 @@ public class InetAddress implements Serializable { } // Otherwise, throw an UnknownHostException. String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error); - addressCache.putUnknownHost(host, detailMessage); + addressCache.putUnknownHost(host, netId, detailMessage); throw gaiException.rethrowAsUnknownHostException(detailMessage); } } diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java index e736c40..9008637 100644 --- a/luni/src/main/java/java/nio/DatagramChannelImpl.java +++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java @@ -130,7 +130,6 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann } } - /** @hide Until ready for a public API change */ @Override synchronized public boolean isConnected() { return connected; diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java index 7185c32..ae33672 100644 --- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java +++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java @@ -55,7 +55,6 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD return socket; } - /** @hide Until ready for a public API change */ @Override public SocketChannel accept() throws IOException { if (!isOpen()) { diff --git a/luni/src/main/java/java/util/IllformedLocaleException.java b/luni/src/main/java/java/util/IllformedLocaleException.java index db1754e..3dec1cd 100644 --- a/luni/src/main/java/java/util/IllformedLocaleException.java +++ b/luni/src/main/java/java/util/IllformedLocaleException.java @@ -21,7 +21,6 @@ package java.util; * * See {@link Locale} and {@link Locale.Builder}. * - * @hide * @since 1.7 */ public class IllformedLocaleException extends RuntimeException { diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java index e0582dc..a8ff15d 100644 --- a/luni/src/main/java/java/util/Locale.java +++ b/luni/src/main/java/java/util/Locale.java @@ -253,7 +253,6 @@ public final class Locale implements Cloneable, Serializable { * * See {@link #getExtension(char)} and {@link Builder#setExtension(char, String)}. * - * @hide * @since 1.7 */ public static final char PRIVATE_USE_EXTENSION = 'x'; @@ -264,7 +263,6 @@ public final class Locale implements Cloneable, Serializable { * * See {@link #getExtension(char)} and {@link Builder#setExtension(char, String)}. * - * @hide * @since 1.7 */ public static final char UNICODE_LOCALE_EXTENSION = 'u'; @@ -302,7 +300,6 @@ public final class Locale implements Cloneable, Serializable { * the structured state (keywords and attributes) specified therein. * * @since 1.7 - * @hide */ public static final class Builder { private String language; @@ -812,7 +809,6 @@ public final class Locale implements Cloneable, Serializable { * * @throws NullPointerException if {@code languageTag} is {@code null}. * - * @hide * @since 1.7 */ public static Locale forLanguageTag(String languageTag) { @@ -1150,6 +1146,8 @@ public final class Locale implements Cloneable, Serializable { * Returns the full variant name in the default {@code Locale} for the variant code of * this {@code Locale}. If there is no matching variant name, the variant code is * returned. + * + * @since 1.7 */ public final String getDisplayVariant() { return getDisplayVariant(getDefault()); @@ -1159,6 +1157,8 @@ public final class Locale implements Cloneable, Serializable { * Returns the full variant name in the specified {@code Locale} for the variant code * of this {@code Locale}. If there is no matching variant name, the variant code is * returned. + * + * @since 1.7 */ public String getDisplayVariant(Locale locale) { if (variantCode.isEmpty()) { @@ -1263,7 +1263,6 @@ public final class Locale implements Cloneable, Serializable { * If set, the script code will be a title cased string of length 4, as per the ISO 15924 * specification. * - * @hide * @since 1.7 */ public String getScript() { @@ -1273,7 +1272,6 @@ public final class Locale implements Cloneable, Serializable { /** * Equivalent to {@code getDisplayScript(Locale.getDefault()))} * - * @hide * @since 1.7 */ public String getDisplayScript() { @@ -1285,7 +1283,6 @@ public final class Locale implements Cloneable, Serializable { * script code is unknown, the return value of this method is the same as that of * {@link #getScript()}. * - * @hide * @since 1.7 */ public String getDisplayScript(Locale locale) { @@ -1321,7 +1318,6 @@ public final class Locale implements Cloneable, Serializable { * For example, we do not require scripts to be a registered ISO 15924 scripts or * languages to appear in the ISO-639-2 code list. * - * @hide * @since 1.7 */ public String toLanguageTag() { @@ -1528,7 +1524,6 @@ public final class Locale implements Cloneable, Serializable { * See <a href="https://tools.ietf.org/html/bcp47#section-2.1"> * the IETF BCP-47 specification</a> (Section 2.2.6) for details. * - * @hide * @since 1.7 */ public Set<Character> getExtensionKeys() { @@ -1543,7 +1538,6 @@ public final class Locale implements Cloneable, Serializable { * locale extension can be fetched using {@link #getUnicodeLocaleAttributes()}, * {@link #getUnicodeLocaleKeys()} and {@link #getUnicodeLocaleType}. * - * @hide * @since 1.7 */ public String getExtension(char extensionKey) { @@ -1556,7 +1550,6 @@ public final class Locale implements Cloneable, Serializable { * For more information about types and keywords, see {@link Builder#setUnicodeLocaleKeyword} * and <a href="http://www.unicode.org/reports/tr35/#BCP47">Unicode Technical Standard #35</a> * - * @hide * @since 1.7 */ public String getUnicodeLocaleType(String keyWord) { @@ -1569,7 +1562,6 @@ public final class Locale implements Cloneable, Serializable { * For more information about attributes, see {@link Builder#addUnicodeLocaleAttribute} * and <a href="http://www.unicode.org/reports/tr35/#BCP47">Unicode Technical Standard #35</a> * - * @hide * @since 1.7 */ public Set<String> getUnicodeLocaleAttributes() { @@ -1582,7 +1574,6 @@ public final class Locale implements Cloneable, Serializable { * For more information about types and keywords, see {@link Builder#setUnicodeLocaleKeyword} * and <a href="http://www.unicode.org/reports/tr35/#BCP47">Unicode Technical Standard #35</a> * - * @hide * @since 1.7 */ public Set<String> getUnicodeLocaleKeys() { diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java index 54b53ae..b38d6a5 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java @@ -56,8 +56,6 @@ import java.util.Queue; * actions subsequent to the access or removal of that element from * the {@code ConcurrentLinkedDeque} in another thread. * - * @hide - * * @since 1.7 * @author Doug Lea * @author Martin Buchholz diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java index 5ac01c8..9448616 100644 --- a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java +++ b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java @@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit; * ForkJoinPool}s may also be appropriate for use with event-style * tasks that are never joined. * - * <p>A static {@link #commonPool()} is available and appropriate for + * <p>A static {@code commonPool()} is available and appropriate for * most applications. The common pool is used by any ForkJoinTask that * is not explicitly submitted to a specified pool. Using the common * pool normally reduces resource usage (its threads are slowly @@ -127,7 +127,6 @@ import java.util.concurrent.TimeUnit; * or internal resources have been exhausted. * * @since 1.7 - * @hide * @author Doug Lea */ public class ForkJoinPool extends AbstractExecutorService { @@ -213,8 +212,7 @@ public class ForkJoinPool extends AbstractExecutorService { * choosing existing queues, and may be randomly repositioned upon * contention with other submitters. In essence, submitters act * like workers except that they are restricted to executing local - * tasks that they submitted (or in the case of CountedCompleters, - * others with the same root task). However, because most + * tasks that they submitted. However, because most * shared/external queue operations are more expensive than * internal, and because, at steady state, external submitters * will compete for CPU with workers, ForkJoinTask.join and @@ -419,12 +417,6 @@ public class ForkJoinPool extends AbstractExecutorService { * to find work (see MAX_HELP) and fall back to suspending the * worker and if necessary replacing it with another. * - * Helping actions for CountedCompleters are much simpler: Method - * helpComplete can take and execute any task with the same root - * as the task being waited on. However, this still entails some - * traversal of completer chains, so is less efficient than using - * CountedCompleters without explicit joins. - * * It is impossible to keep exactly the target parallelism number * of threads running at any given time. Determining the * existence of conservatively safe helping targets, the @@ -2907,7 +2899,7 @@ public class ForkJoinPool extends AbstractExecutorService { * Possibly initiates an orderly shutdown in which previously * submitted tasks are executed, but no new tasks will be * accepted. Invocation has no effect on execution state if this - * is the {@link #commonPool()}, and no additional effect if + * is the {@code commonPool()}, and no additional effect if * already shut down. Tasks that are in the process of being * submitted concurrently during the course of this method may or * may not be rejected. @@ -2920,7 +2912,7 @@ public class ForkJoinPool extends AbstractExecutorService { /** * Possibly attempts to cancel and/or stop all tasks, and reject * all subsequently submitted tasks. Invocation has no effect on - * execution state if this is the {@link #commonPool()}, and no + * execution state if this is the {@code commonPool()}, and no * additional effect if already shut down. Otherwise, tasks that * are in the process of being submitted or executed concurrently * during the course of this method may or may not be @@ -2979,8 +2971,8 @@ public class ForkJoinPool extends AbstractExecutorService { /** * Blocks until all tasks have completed execution after a * shutdown request, or the timeout occurs, or the current thread - * is interrupted, whichever happens first. Because the {@link - * #commonPool()} never terminates until program shutdown, when + * is interrupted, whichever happens first. Because the {@code + * commonPool()} never terminates until program shutdown, when * applied to the common pool, this method is equivalent to {@link * #awaitQuiescence(long, TimeUnit)} but always returns {@code false}. * @@ -3064,7 +3056,7 @@ public class ForkJoinPool extends AbstractExecutorService { /** * Waits and/or attempts to assist performing tasks indefinitely - * until the {@link #commonPool()} {@link #isQuiescent}. + * until the {@code commonPool()} {@link #isQuiescent}. */ static void quiesceCommonPool() { common.awaitQuiescence(Long.MAX_VALUE, TimeUnit.NANOSECONDS); diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java index 6d25775..c6bc6de 100644 --- a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java +++ b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java @@ -32,8 +32,8 @@ import java.lang.reflect.Constructor; * * <p>A "main" {@code ForkJoinTask} begins execution when it is * explicitly submitted to a {@link ForkJoinPool}, or, if not already - * engaged in a ForkJoin computation, commenced in the {@link - * ForkJoinPool#commonPool()} via {@link #fork}, {@link #invoke}, or + * engaged in a ForkJoin computation, commenced in the {@code + * ForkJoinPool.commonPool()} via {@link #fork}, {@link #invoke}, or * related methods. Once started, it will usually in turn start other * subtasks. As indicated by the name of this class, many programs * using {@code ForkJoinTask} employ only methods {@link #fork} and @@ -74,10 +74,9 @@ import java.lang.reflect.Constructor; * but doing do requires three further considerations: (1) Completion * of few if any <em>other</em> tasks should be dependent on a task * that blocks on external synchronization or I/O. Event-style async - * tasks that are never joined (for example, those subclassing {@link - * CountedCompleter}) often fall into this category. (2) To minimize - * resource impact, tasks should be small; ideally performing only the - * (possibly) blocking action. (3) Unless the {@link + * tasks that are never joined often fall into this category. + * (2) To minimize resource impact, tasks should be small; ideally + * performing only the (possibly) blocking action. (3) Unless the {@link * ForkJoinPool.ManagedBlocker} API is used, or the number of possibly * blocked tasks is known to be less than the pool's {@link * ForkJoinPool#getParallelism} level, the pool cannot guarantee that @@ -120,13 +119,11 @@ import java.lang.reflect.Constructor; * <p>The ForkJoinTask class is not usually directly subclassed. * Instead, you subclass one of the abstract classes that support a * particular style of fork/join processing, typically {@link - * RecursiveAction} for most computations that do not return results, - * {@link RecursiveTask} for those that do, and {@link - * CountedCompleter} for those in which completed actions trigger - * other actions. Normally, a concrete ForkJoinTask subclass declares - * fields comprising its parameters, established in a constructor, and - * then defines a {@code compute} method that somehow uses the control - * methods supplied by this base class. + * RecursiveAction} for most computations that do not return results + * and {@link RecursiveTask} for those that do. Normally, a concrete + * ForkJoinTask subclass declares fields comprising its parameters, + * established in a constructor, and then defines a {@code compute} + * method that somehow uses the control methods supplied by this base class. * * <p>Method {@link #join} and its variants are appropriate for use * only when completion dependencies are acyclic; that is, the @@ -138,9 +135,9 @@ import java.lang.reflect.Constructor; * may be of use in constructing custom subclasses for problems that * are not statically structured as DAGs. To support such usages, a * ForkJoinTask may be atomically <em>tagged</em> with a {@code short} - * value using {@link #setForkJoinTaskTag} or {@link - * #compareAndSetForkJoinTaskTag} and checked using {@link - * #getForkJoinTaskTag}. The ForkJoinTask implementation does not use + * value using {@code setForkJoinTaskTag} or {@code + * compareAndSetForkJoinTaskTag} and checked using {@code + * getForkJoinTaskTag}. The ForkJoinTask implementation does not use * these {@code protected} methods or tags for any purpose, but they * may be of use in the construction of specialized subclasses. For * example, parallel graph traversals can use the supplied methods to @@ -178,7 +175,6 @@ import java.lang.reflect.Constructor; * execution. Serialization is not relied on during execution itself. * * @since 1.7 - * @hide * @author Doug Lea */ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { @@ -645,8 +641,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { /** * Arranges to asynchronously execute this task in the pool the - * current task is running in, if applicable, or using the {@link - * ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}. While + * current task is running in, if applicable, or using the {@code + * ForkJoinPool.commonPool()} if not {@link #inForkJoinPool}. While * it is not necessarily enforced, it is a usage error to fork a * task more than once unless it has completed and been * reinitialized. Subsequent modifications to the state of this diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java index 5f2799b..ae28700 100644 --- a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java +++ b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java @@ -18,7 +18,6 @@ package java.util.concurrent; * {@linkplain ForkJoinPool#ForkJoinPool use it} in a {@code ForkJoinPool}. * * @since 1.7 - * @hide * @author Doug Lea */ public class ForkJoinWorkerThread extends Thread { diff --git a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java index cff5dbf..a041fb1 100644 --- a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java +++ b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java @@ -50,7 +50,6 @@ import java.util.concurrent.locks.LockSupport; * the {@code LinkedTransferQueue} in another thread. * * @since 1.7 - * @hide * @author Doug Lea * @param <E> the type of elements held in this collection */ diff --git a/luni/src/main/java/java/util/concurrent/Phaser.java b/luni/src/main/java/java/util/concurrent/Phaser.java index a9adbe5..a97d187 100644 --- a/luni/src/main/java/java/util/concurrent/Phaser.java +++ b/luni/src/main/java/java/util/concurrent/Phaser.java @@ -227,7 +227,6 @@ import java.util.concurrent.locks.LockSupport; * of participants. * * @since 1.7 - * @hide * @author Doug Lea */ public class Phaser { diff --git a/luni/src/main/java/java/util/concurrent/RecursiveAction.java b/luni/src/main/java/java/util/concurrent/RecursiveAction.java index 8d666f6..e3a6340 100644 --- a/luni/src/main/java/java/util/concurrent/RecursiveAction.java +++ b/luni/src/main/java/java/util/concurrent/RecursiveAction.java @@ -131,7 +131,6 @@ package java.util.concurrent; * }}</pre> * * @since 1.7 - * @hide * @author Doug Lea */ public abstract class RecursiveAction extends ForkJoinTask<Void> { diff --git a/luni/src/main/java/java/util/concurrent/RecursiveTask.java b/luni/src/main/java/java/util/concurrent/RecursiveTask.java index 421c9d3..80baa52 100644 --- a/luni/src/main/java/java/util/concurrent/RecursiveTask.java +++ b/luni/src/main/java/java/util/concurrent/RecursiveTask.java @@ -34,7 +34,6 @@ package java.util.concurrent; * sequentially solve rather than subdividing. * * @since 1.7 - * @hide * @author Doug Lea */ public abstract class RecursiveTask<V> extends ForkJoinTask<V> { diff --git a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java index a52351b..483981d 100644 --- a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java +++ b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java @@ -690,7 +690,6 @@ public class ScheduledThreadPoolExecutor * @param value if {@code true}, remove on cancellation, else don't * @see #getRemoveOnCancelPolicy * @since 1.7 - * @hide */ public void setRemoveOnCancelPolicy(boolean value) { removeOnCancel = value; @@ -705,7 +704,6 @@ public class ScheduledThreadPoolExecutor * from the queue * @see #setRemoveOnCancelPolicy * @since 1.7 - * @hide */ public boolean getRemoveOnCancelPolicy() { return removeOnCancel; diff --git a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java index a559321..5baf75f 100644 --- a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java +++ b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java @@ -30,7 +30,6 @@ import java.util.Random; * generation methods. * * @since 1.7 - * @hide * @author Doug Lea */ public class ThreadLocalRandom extends Random { diff --git a/luni/src/main/java/java/util/concurrent/TransferQueue.java b/luni/src/main/java/java/util/concurrent/TransferQueue.java index 9cd5773..4c2be6f 100644 --- a/luni/src/main/java/java/util/concurrent/TransferQueue.java +++ b/luni/src/main/java/java/util/concurrent/TransferQueue.java @@ -33,7 +33,6 @@ package java.util.concurrent; * and {@code transfer} are effectively synonymous. * * @since 1.7 - * @hide * @author Doug Lea * @param <E> the type of elements held in this collection */ diff --git a/luni/src/main/java/java/util/concurrent/atomic/Fences.java b/luni/src/main/java/java/util/concurrent/atomic/Fences.java index 7ecf45a..5714ba0 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/Fences.java +++ b/luni/src/main/java/java/util/concurrent/atomic/Fences.java @@ -453,7 +453,6 @@ package java.util.concurrent.atomic; * * </dl> * - * @since 1.7 * @hide * @author Doug Lea */ diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index 4c5e280..37aa9d0 100644 --- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -1255,7 +1255,6 @@ public abstract class AbstractQueuedLongSynchronizer * current thread, and {@code false} if the current thread * is at the head of the queue or the queue is empty * @since 1.7 - * @hide */ public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 0350060..e711da5 100644 --- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -1485,7 +1485,6 @@ public abstract class AbstractQueuedSynchronizer * current thread, and {@code false} if the current thread * is at the head of the queue or the queue is empty * @since 1.7 - * @hide */ public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized diff --git a/luni/src/main/java/java/util/jar/JarEntry.java b/luni/src/main/java/java/util/jar/JarEntry.java index 85c8678..bceef63 100644 --- a/luni/src/main/java/java/util/jar/JarEntry.java +++ b/luni/src/main/java/java/util/jar/JarEntry.java @@ -17,16 +17,16 @@ package java.util.jar; -import javax.security.auth.x500.X500Principal; import java.io.IOException; import java.security.CodeSigner; import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; import java.util.zip.ZipEntry; /** @@ -114,8 +114,12 @@ public class JarEntry extends ZipEntry { * entry or {@code null} if none exists. Make sure that the everything is * read from the input stream before calling this method, or else the method * returns {@code null}. + * <p> + * This method returns all the signers' unverified chains concatenated + * together in one array. To know which certificates were tied to the + * private keys that made the signatures on this entry, see + * {@link #getCodeSigners()} instead. * - * @return the certificate for this entry. * @see java.security.cert.Certificate */ public Certificate[] getCertificates() { @@ -126,7 +130,27 @@ public class JarEntry extends ZipEntry { if (jarVerifier == null) { return null; } - return jarVerifier.getCertificates(getName()); + + Certificate[][] certChains = jarVerifier.getCertificateChains(getName()); + if (certChains == null) { + return null; + } + + // Measure number of certs. + int count = 0; + for (Certificate[] chain : certChains) { + count += chain.length; + } + + // Create new array and copy all the certs into it. + Certificate[] certs = new Certificate[count]; + int i = 0; + for (Certificate[] chain : certChains) { + System.arraycopy(chain, 0, certs, i, chain.length); + i += chain.length; + } + + return certs; } void setAttributes(Attributes attrib) { @@ -138,68 +162,60 @@ public class JarEntry extends ZipEntry { * JAR file. If there is no such code signer, it returns {@code null}. Make * sure that the everything is read from the input stream before calling * this method, or else the method returns {@code null}. + * <p> + * Only the digital signature on the entry is cryptographically verified. + * None of the certificates in the the {@link CertPath} returned from + * {@link CodeSigner#getSignerCertPath()} are verified and must be verified + * by the caller if needed. See {@link CertPathValidator} for more + * information. * - * @return the code signers for the JAR entry. + * @return an array of CodeSigner for this JAR entry. * @see CodeSigner */ public CodeSigner[] getCodeSigners() { + if (parentJar == null) { + return null; + } + + JarVerifier jarVerifier = parentJar.verifier; + if (jarVerifier == null) { + return null; + } + if (signers == null) { - signers = getCodeSigners(getCertificates()); + signers = getCodeSigners(jarVerifier.getCertificateChains(getName())); } if (signers == null) { return null; } - CodeSigner[] tmp = new CodeSigner[signers.length]; - System.arraycopy(signers, 0, tmp, 0, tmp.length); - return tmp; + return signers.clone(); } - private CodeSigner[] getCodeSigners(Certificate[] certs) { - if (certs == null) { + private CodeSigner[] getCodeSigners(Certificate[][] certChains) { + if (certChains == null) { return null; } - X500Principal prevIssuer = null; - ArrayList<Certificate> list = new ArrayList<Certificate>(certs.length); - ArrayList<CodeSigner> asigners = new ArrayList<CodeSigner>(); + ArrayList<CodeSigner> asigners = new ArrayList<CodeSigner>(certChains.length); - for (Certificate element : certs) { - if (!(element instanceof X509Certificate)) { - // Only X509Certificate-s are taken into account - see API spec. - continue; - } - X509Certificate x509 = (X509Certificate) element; - if (prevIssuer != null) { - X500Principal subj = x509.getSubjectX500Principal(); - if (!prevIssuer.equals(subj)) { - // Ok, this ends the previous chain, - // so transform this one into CertPath ... - addCodeSigner(asigners, list); - // ... and start a new one - list.clear(); - }// else { it's still the same chain } - - } - prevIssuer = x509.getIssuerX500Principal(); - list.add(x509); - } - if (!list.isEmpty()) { - addCodeSigner(asigners, list); - } - if (asigners.isEmpty()) { - // 'signers' is 'null' already - return null; + for (Certificate[] chain : certChains) { + addCodeSigner(asigners, chain); } CodeSigner[] tmp = new CodeSigner[asigners.size()]; asigners.toArray(tmp); return tmp; - } - private void addCodeSigner(ArrayList<CodeSigner> asigners, - List<Certificate> list) { + private void addCodeSigner(ArrayList<CodeSigner> asigners, Certificate[] certs) { + for (Certificate cert : certs) { + // Only X509Certificate instances are counted. See API spec. + if (!(cert instanceof X509Certificate)) { + return; + } + } + CertPath certPath = null; if (!isFactoryChecked) { try { @@ -214,7 +230,7 @@ public class JarEntry extends ZipEntry { return; } try { - certPath = factory.generateCertPath(list); + certPath = factory.generateCertPath(Arrays.asList(certs)); } catch (CertificateException ex) { // do nothing } diff --git a/luni/src/main/java/java/util/jar/JarVerifier.java b/luni/src/main/java/java/util/jar/JarVerifier.java index c545a02..467e298 100644 --- a/luni/src/main/java/java/util/jar/JarVerifier.java +++ b/luni/src/main/java/java/util/jar/JarVerifier.java @@ -27,7 +27,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; @@ -72,8 +71,8 @@ class JarVerifier { private final Hashtable<String, Certificate[]> certificates = new Hashtable<String, Certificate[]>(5); - private final Hashtable<String, Certificate[]> verifiedEntries = - new Hashtable<String, Certificate[]>(); + private final Hashtable<String, Certificate[][]> verifiedEntries = + new Hashtable<String, Certificate[][]>(); /** * Stores and a hash and a message digest and verifies that massage digest @@ -87,16 +86,16 @@ class JarVerifier { private final byte[] hash; - private final Certificate[] certificates; + private final Certificate[][] certChains; - private final Hashtable<String, Certificate[]> verifiedEntries; + private final Hashtable<String, Certificate[][]> verifiedEntries; VerifierEntry(String name, MessageDigest digest, byte[] hash, - Certificate[] certificates, Hashtable<String, Certificate[]> verifedEntries) { + Certificate[][] certChains, Hashtable<String, Certificate[][]> verifedEntries) { this.name = name; this.digest = digest; this.hash = hash; - this.certificates = certificates; + this.certChains = certChains; this.verifiedEntries = verifedEntries; } @@ -132,7 +131,7 @@ class JarVerifier { if (!MessageDigest.isEqual(d, Base64.decode(hash))) { throw invalidDigest(JarFile.MANIFEST_NAME, name, name); } - verifiedEntries.put(name, certificates); + verifiedEntries.put(name, certChains); } } @@ -185,7 +184,7 @@ class JarVerifier { return null; } - ArrayList<Certificate> certs = new ArrayList<Certificate>(); + ArrayList<Certificate[]> certChains = new ArrayList<Certificate[]>(); Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, HashMap<String, Attributes>> entry = it.next(); @@ -195,16 +194,16 @@ class JarVerifier { String signatureFile = entry.getKey(); Certificate[] certChain = certificates.get(signatureFile); if (certChain != null) { - Collections.addAll(certs, certChain); + certChains.add(certChain); } } } // entry is not signed - if (certs.isEmpty()) { + if (certChains.isEmpty()) { return null; } - Certificate[] certificatesArray = certs.toArray(new Certificate[certs.size()]); + Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]); for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) { final String algorithm = DIGEST_ALGORITHMS[i]; @@ -216,9 +215,8 @@ class JarVerifier { try { return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes, - certificatesArray, verifiedEntries); - } catch (NoSuchAlgorithmException e) { - // ignored + certChainsArray, verifiedEntries); + } catch (NoSuchAlgorithmException ignored) { } } return null; @@ -396,20 +394,16 @@ class JarVerifier { } /** - * Returns all of the {@link java.security.cert.Certificate} instances that + * Returns all of the {@link java.security.cert.Certificate} chains that * were used to verify the signature on the JAR entry called - * {@code name}. + * {@code name}. Callers must not modify the returned arrays. * * @param name * the name of a JAR entry. - * @return an array of {@link java.security.cert.Certificate}. + * @return an array of {@link java.security.cert.Certificate} chains. */ - Certificate[] getCertificates(String name) { - Certificate[] verifiedCerts = verifiedEntries.get(name); - if (verifiedCerts == null) { - return null; - } - return verifiedCerts.clone(); + Certificate[][] getCertificateChains(String name) { + return verifiedEntries.get(name); } /** diff --git a/luni/src/main/java/java/util/jar/StrictJarFile.java b/luni/src/main/java/java/util/jar/StrictJarFile.java index fa175d8..4a8af5f 100644 --- a/luni/src/main/java/java/util/jar/StrictJarFile.java +++ b/luni/src/main/java/java/util/jar/StrictJarFile.java @@ -87,16 +87,51 @@ public final class StrictJarFile { } /** + * Return all certificate chains for a given {@link ZipEntry} belonging to this jar. + * This method MUST be called only after fully exhausting the InputStream belonging + * to this entry. + * + * Returns {@code null} if this jar file isn't signed or if this method is + * called before the stream is processed. + */ + public Certificate[][] getCertificateChains(ZipEntry ze) { + if (isSigned) { + return verifier.getCertificateChains(ze.getName()); + } + + return null; + } + + /** * Return all certificates for a given {@link ZipEntry} belonging to this jar. * This method MUST be called only after fully exhausting the InputStream belonging * to this entry. * * Returns {@code null} if this jar file isn't signed or if this method is * called before the stream is processed. + * + * @deprecated Switch callers to use getCertificateChains instead */ + @Deprecated public Certificate[] getCertificates(ZipEntry ze) { if (isSigned) { - return verifier.getCertificates(ze.getName()); + Certificate[][] certChains = verifier.getCertificateChains(ze.getName()); + + // Measure number of certs. + int count = 0; + for (Certificate[] chain : certChains) { + count += chain.length; + } + + // Create new array and copy all the certs into it. + Certificate[] certs = new Certificate[count]; + int i = 0; + for (Certificate[] chain : certChains) { + System.arraycopy(chain, 0, certs, i, chain.length); + i += chain.length; + } + + return certs; } return null; diff --git a/luni/src/main/java/javax/net/ssl/DistinguishedNameParser.java b/luni/src/main/java/javax/net/ssl/DistinguishedNameParser.java index c3c1606..25ab76f 100644 --- a/luni/src/main/java/javax/net/ssl/DistinguishedNameParser.java +++ b/luni/src/main/java/javax/net/ssl/DistinguishedNameParser.java @@ -17,6 +17,9 @@ package javax.net.ssl; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import javax.security.auth.x500.X500Principal; /** @@ -406,4 +409,71 @@ public final class DistinguishedNameParser { } } } + + /** + * Parses the DN and returns all values for an attribute type, in + * the order of decreasing significance (most significant first). + * + * @param attributeType attribute type to look for (e.g. "ca") + */ + public List<String> getAllMostSpecificFirst(String attributeType) { + // Initialize internal state. + pos = 0; + beg = 0; + end = 0; + cur = 0; + chars = dn.toCharArray(); + List<String> result = Collections.emptyList(); + + String attType = nextAT(); + if (attType == null) { + return result; + } + while (pos < length) { + String attValue = ""; + + switch (chars[pos]) { + case '"': + attValue = quotedAV(); + break; + case '#': + attValue = hexAV(); + break; + case '+': + case ',': + case ';': // compatibility with RFC 1779: semicolon can separate RDNs + //empty attribute value + break; + default: + attValue = escapedAV(); + } + + // Values are ordered from most specific to least specific + // due to the RFC2253 formatting. So take the first match + // we see. + if (attributeType.equalsIgnoreCase(attType)) { + if (result.isEmpty()) { + result = new ArrayList<String>(); + } + result.add(attValue); + } + + if (pos >= length) { + break; + } + + if (chars[pos] == ',' || chars[pos] == ';') { + } else if (chars[pos] != '+') { + throw new IllegalStateException("Malformed DN: " + dn); + } + + pos++; + attType = nextAT(); + if (attType == null) { + throw new IllegalStateException("Malformed DN: " + dn); + } + } + + return result; + } } diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java index d09e442..bf4b448 100644 --- a/luni/src/main/java/libcore/io/ForwardingOs.java +++ b/luni/src/main/java/libcore/io/ForwardingOs.java @@ -52,6 +52,7 @@ public class ForwardingOs implements Os { public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); } public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); } + public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); } public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); } public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); } public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); } @@ -73,7 +74,6 @@ public class ForwardingOs implements Os { public void fsync(FileDescriptor fd) throws ErrnoException { os.fsync(fd); } public void ftruncate(FileDescriptor fd, long length) throws ErrnoException { os.ftruncate(fd, length); } public String gai_strerror(int error) { return os.gai_strerror(error); } - public InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException { return os.getaddrinfo(node, hints); } public int getegid() { return os.getegid(); } public int geteuid() { return os.geteuid(); } public int getgid() { return os.getgid(); } diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java index a537aeb..511bb27 100644 --- a/luni/src/main/java/libcore/io/Os.java +++ b/luni/src/main/java/libcore/io/Os.java @@ -43,6 +43,7 @@ import java.nio.ByteBuffer; public interface Os { public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException; public boolean access(String path, int mode) throws ErrnoException; + public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException; public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; public void chmod(String path, int mode) throws ErrnoException; public void chown(String path, int uid, int gid) throws ErrnoException; @@ -64,7 +65,6 @@ public interface Os { public void fsync(FileDescriptor fd) throws ErrnoException; public void ftruncate(FileDescriptor fd, long length) throws ErrnoException; public String gai_strerror(int error); - public InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException; public int getegid(); public int geteuid(); public int getgid(); diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java index 7551190..f5eaaa3 100644 --- a/luni/src/main/java/libcore/io/Posix.java +++ b/luni/src/main/java/libcore/io/Posix.java @@ -46,6 +46,7 @@ public final class Posix implements Os { public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException; public native boolean access(String path, int mode) throws ErrnoException; + public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException; public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; public native void chmod(String path, int mode) throws ErrnoException; public native void chown(String path, int uid, int gid) throws ErrnoException; @@ -67,7 +68,6 @@ public final class Posix implements Os { public native void fsync(FileDescriptor fd) throws ErrnoException; public native void ftruncate(FileDescriptor fd, long length) throws ErrnoException; public native String gai_strerror(int error); - public native InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException; public native int getegid(); public native int geteuid(); public native int getgid(); diff --git a/luni/src/main/native/Portability.h b/luni/src/main/native/Portability.h index fb60ed4..1520311 100644 --- a/luni/src/main/native/Portability.h +++ b/luni/src/main/native/Portability.h @@ -65,7 +65,7 @@ static inline int mincore(void* addr, size_t length, unsigned char* vec) { #include <sys/param.h> #include <sys/mount.h> -#else +#else // defined(__APPLE__) // Bionic or glibc. @@ -73,6 +73,15 @@ static inline int mincore(void* addr, size_t length, unsigned char* vec) { #include <sys/sendfile.h> #include <sys/statvfs.h> -#endif +#endif // defined(__APPLE__) + +#if !defined(__BIONIC__) +#include <netdb.h> +#include "../../bionic/libc/dns/include/resolv_netid.h" +inline int android_getaddrinfofornet(const char *hostname, const char *servname, + const struct addrinfo *hints, unsigned /*netid*/, unsigned /*mark*/, struct addrinfo **res) { + return getaddrinfo(hostname, servname, hints, res); +} +#endif // !defined(__BIONIC__) #endif // PORTABILITY_H_included diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp index 51dd8a1..e8e8efb 100644 --- a/luni/src/main/native/libcore_io_Posix.cpp +++ b/luni/src/main/native/libcore_io_Posix.cpp @@ -25,6 +25,7 @@ #include "NetworkUtilities.h" #include "Portability.h" #include "readlink.h" +#include "../../bionic/libc/dns/include/resolv_netid.h" // For android_getaddrinfofornet. #include "ScopedBytes.h" #include "ScopedLocalRef.h" #include "ScopedPrimitiveArray.h" @@ -661,7 +662,8 @@ static jstring Posix_gai_strerror(JNIEnv* env, jobject, jint error) { return env->NewStringUTF(gai_strerror(error)); } -static jobjectArray Posix_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, jobject javaHints) { +static jobjectArray Posix_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, + jobject javaHints, jint netId) { ScopedUtfChars node(env, javaNode); if (node.c_str() == NULL) { return NULL; @@ -681,10 +683,10 @@ static jobjectArray Posix_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, jo addrinfo* addressList = NULL; errno = 0; - int rc = getaddrinfo(node.c_str(), NULL, &hints, &addressList); + int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList); UniquePtr<addrinfo, addrinfo_deleter> addressListDeleter(addressList); if (rc != 0) { - throwGaiException(env, "getaddrinfo", rc); + throwGaiException(env, "android_getaddrinfo", rc); return NULL; } @@ -694,7 +696,7 @@ static jobjectArray Posix_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, jo if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { ++addressCount; } else { - ALOGE("getaddrinfo unexpected ai_family %i", ai->ai_family); + ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family); } } if (addressCount == 0) { @@ -712,7 +714,7 @@ static jobjectArray Posix_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, jo for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { // Unknown address family. Skip this address. - ALOGE("getaddrinfo unexpected ai_family %i", ai->ai_family); + ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family); continue; } @@ -1537,6 +1539,7 @@ static jint Posix_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buff static JNINativeMethod gMethods[] = { NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"), NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"), + NATIVE_METHOD(Posix, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"), NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"), NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"), NATIVE_METHOD(Posix, chown, "(Ljava/lang/String;II)V"), @@ -1558,7 +1561,6 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Posix, fsync, "(Ljava/io/FileDescriptor;)V"), NATIVE_METHOD(Posix, ftruncate, "(Ljava/io/FileDescriptor;J)V"), NATIVE_METHOD(Posix, gai_strerror, "(I)Ljava/lang/String;"), - NATIVE_METHOD(Posix, getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;)[Ljava/net/InetAddress;"), NATIVE_METHOD(Posix, getegid, "()I"), NATIVE_METHOD(Posix, geteuid, "()I"), NATIVE_METHOD(Posix, getgid, "()I"), diff --git a/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java index 6d033d3..1475a63 100644 --- a/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java +++ b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java @@ -109,6 +109,24 @@ public class CertBlacklistTest extends TestCase { "3xQAyMuOHm72exJljYFqIsiNvGE0KufCqCuH1PD97IXMrLlwGmKKg5jP349lySBpJjm6RDqCTT+6" + "dUl2jkVbeNmco99Y7AOdtLsOdXBMCo5x8lK8zwQWFrzEms0joHXCpWfGWA=="; + public static final String ANSSI = "" + + "MIIDbDCCAlSgAwIBAgIDAx2nMA0GCSqGSIb3DQEBBQUAMEsxCzAJBgNVBAYTAkZSMQ4wDAYDVQQK" + + "EwVER1RQRTEsMCoGA1UEAxMjQUMgREdUUEUgU2lnbmF0dXJlIEF1dGhlbnRpZmljYXRpb24wHhcN" + + "MTMwNzE4MTAwNTI4WhcNMTQwNzE4MTAwNTI4WjA+MQswCQYDVQQGEwJGUjETMBEGA1UECgwKREcg" + + "VHLDqXNvcjEaMBgGA1UEAwwRQUMgREcgVHLDqXNvciBTU0wwggEiMA0GCSqGSIb3DQEBAQUAA4IB" + + "DwAwggEKAoIBAQDI0WFSUyY+MmtFkqFjTefoFyDgh9b1C/2YvSIvT8oCH62JWT5rpeTCZwaXbqWc" + + "jaNfzggqaFsokqfhBif43HNHNtNJmvKE32VcuLB0SpsLR/1VeTd9F99C1JeHVa+nelumOHEfouX8" + + "rRFrxNXNIYTVeiENT8Y2YqRb/XAril9g7i674uFzLiNR/t/N/F8Exujv9U8m8rmgud/+tG9WDRaD" + + "Jwoj3ZFCOnL5qLnSUEcS6TzWpozLmC2JVO5GZKGGd7qC9FjdBkVilkbVIEGSrYvz2Uz2v5IGqMBI" + + "QaFL/kSYWxGTaedTOk2drFEApp9AEPTfv1NwCWBfegsGQrHUROM3AgMBAAGjZjBkMBIGA1UdEwEB" + + "/wQIMAYBAf8CAQQwHQYDVR0OBBYEFAAMW8lJqJW0DtAv5p3Mjogxvh9lMB8GA1UdIwQYMBaAFOnb" + + "kI/9W5nkFTvwYlyn5A1Y6IeZMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAtDfG" + + "HkHOLW2d9fiMtwtkEwDauISJLJyCjoRmawzmQbIZXq7HaLliVfE0sdfKUm0iQ0im1/CpnJLPoTeK" + + "yBHvNu1ubLc2m+9dabAYhF3pVdKC+gNaAzBXZ9Gt0p1CLk1lf8Hg+R10HN2IPCv7V/crz2Ga+c23" + + "4P3pfwYW8+Nd7alGCuvqot6UYXOlheF7zWUkHn6z6tvY+9oMDHKSUAthhA/FB50JgJU89zyTv1eg" + + "Y3ldKwvYBW3W3yNZdTHbPyNsPJdhqA55mDNsteE5YTp1PyySDb1MSVrbxDEruoH6ZE99Hob4Ih8A" + + "mn7MHZatGClECgjXWFZ2Gxa7OUCaQpcH8g=="; + public CertBlacklistTest() throws IOException { tmpFile = File.createTempFile("test", ""); DEFAULT_PUBKEYS = getDefaultPubkeys(); @@ -415,6 +433,20 @@ public class CertBlacklistTest extends TestCase { assertEquals(bl.isPublicKeyBlackListed(pk), true); } + public void testANSSISerialBlacklist() throws Exception { + CertBlacklist bl = new CertBlacklist(); + assertEquals(bl.isSerialNumberBlackListed(createSerialNumber(ANSSI)), true); + } + + public void testANSSIIntermediatePubkeyBlacklist() throws Exception { + // build the public key + PublicKey pk = createPublicKey(ANSSI); + // set our blacklist path + CertBlacklist bl = new CertBlacklist(); + // check to make sure it isn't blacklisted + assertEquals(bl.isPublicKeyBlackListed(pk), true); + } + private static void printHash(String cert) throws Exception { System.out.println("CERTIFICATE PUBLIC KEY HASH: " + getHash(createPublicKey(cert))); } diff --git a/luni/src/test/java/libcore/java/io/SerializationTest.java b/luni/src/test/java/libcore/java/io/SerializationTest.java index d452c11..1002cf1 100644 --- a/luni/src/test/java/libcore/java/io/SerializationTest.java +++ b/luni/src/test/java/libcore/java/io/SerializationTest.java @@ -16,12 +16,15 @@ package libcore.java.io; -import java.io.IOException; +import junit.framework.TestCase; + import java.io.InvalidClassException; +import java.io.InvalidObjectException; import java.io.ObjectStreamClass; import java.io.ObjectStreamField; import java.io.Serializable; -import junit.framework.TestCase; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; import libcore.util.SerializationTester; public final class SerializationTest extends TestCase { @@ -61,6 +64,7 @@ public final class SerializationTest extends TestCase { } } + @SuppressWarnings("unused") // Required for deserialization test static class SerialVersionUidChanged implements Serializable { private static final long serialVersionUID = 1L; // was 0L private int a; @@ -77,7 +81,259 @@ public final class SerializationTest extends TestCase { } } + @SuppressWarnings("unused") // Required for deserialization test static class FieldsChanged implements Serializable { private int b; // was 'a' } + + public static boolean wasSerializableInitializedFlag = false; + + @SuppressWarnings("unused") // Required for deserialization test. + public static class WasSerializable /* implements java.io.Serializable */ { + static final long serialVersionUID = 0L; + static { + SerializationTest.wasSerializableInitializedFlag = true; + } + private int i; + } + + public void testDeserializeWasSerializableClass() throws Exception { + // This was created by serializing a WasSerializable when it was serializable. + // String s = SerializationTester.serializeHex(new WasSerializable()); + final String s = "aced0005737200316c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6" + + "e546573742457617353657269616c697a61626c65000000000000000002000149000169787000000" + + "000"; + + wasSerializableInitializedFlag = false; + try { + SerializationTester.deserializeHex(s); + fail(); + } catch (InvalidClassException expected) { + } + assertFalse(wasSerializableInitializedFlag); + } + + // The WasExternalizable class before it was modified. + /* + public static class WasExternalizable implements Externalizable { + static final long serialVersionUID = 0L; + + @Override + public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException { + + } + + @Override + public void writeExternal(ObjectOutput output) throws IOException { + + } + } + */ + + public static boolean wasExternalizableInitializedFlag = false; + + @SuppressWarnings("unused") // Required for deserialization test + public static class WasExternalizable implements Serializable { + static final long serialVersionUID = 0L; + static { + SerializationTest.wasExternalizableInitializedFlag = true; + } + + } + + public void testDeserializeWasExternalizableClass() throws Exception { + // This was created by serializing a WasExternalizable when it was externalizable. + // String s = SerializationTester.serializeHex(new WasExternalizable()); + final String s = "aced0005737200336c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6" + + "e546573742457617345787465726e616c697a61626c6500000000000000000c0000787078"; + + wasExternalizableInitializedFlag = false; + try { + SerializationTester.deserializeHex(s); + fail(); + } catch (InvalidClassException expected) { + } + // Unlike other similar tests static initialization will take place if the local class is + // Serializable or Externalizable because serialVersionUID field is accessed. + // The RI appears to do the same. + assertTrue(wasExternalizableInitializedFlag); + } + + // The WasEnum class before it was modified. + /* + public enum WasEnum { + VALUE + } + */ + + public static boolean wasEnumInitializedFlag = false; + + @SuppressWarnings("unused") // Required for deserialization test + public static class WasEnum { + static final long serialVersionUID = 0L; + static { + SerializationTest.wasEnumInitializedFlag = true; + } + } + + public void testDeserializeWasEnum() throws Exception { + // This was created by serializing a WasEnum when it was an enum. + // String s = SerializationTester.serializeHex(WasEnum.VALUE); + final String s = "aced00057e7200296c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6" + + "e5465737424576173456e756d00000000000000001200007872000e6a6176612e6c616e672e456e7" + + "56d0000000000000000120000787074000556414c5545"; + + wasEnumInitializedFlag = false; + try { + SerializationTester.deserializeHex(s); + fail(); + } catch (InvalidClassException expected) { + } + assertFalse(wasEnumInitializedFlag); + } + + // The WasObject class before it was modified. + /* + public static class WasObject implements java.io.Serializable { + static final long serialVersionUID = 0L; + private int i; + } + */ + + public static boolean wasObjectInitializedFlag; + + @SuppressWarnings("unused") // Required for deserialization test + public enum WasObject { + VALUE; + + static { + SerializationTest.wasObjectInitializedFlag = true; + } + } + + public void testDeserializeWasObject() throws Exception { + // This was created by serializing a WasObject when it wasn't yet an enum. + // String s = SerializationTester.serializeHex(new WasObject()); + final String s = "aced00057372002b6c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6" + + "e54657374245761734f626a656374000000000000000002000149000169787000000000"; + + wasObjectInitializedFlag = false; + try { + SerializationTester.deserializeHex(s); + fail(); + } catch (InvalidClassException expected) { + } + assertFalse(wasObjectInitializedFlag); + } + + @SuppressWarnings("unused") // Required for deserialization test + public enum EnumMissingValue { + /*MISSING_VALUE*/ + } + + public void testDeserializeEnumMissingValue() throws Exception { + // This was created by serializing a EnumMissingValue when it had MISSING_VALUE. + // String s = SerializationTester.serializeHex(EnumMissingValue.MISSING_VALUE); + final String s = "aced00057e7200326c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6" + + "e5465737424456e756d4d697373696e6756616c756500000000000000001200007872000e6a61766" + + "12e6c616e672e456e756d0000000000000000120000787074000d4d495353494e475f56414c5545"; + + try { + SerializationTester.deserializeHex(s); + fail(); + } catch (InvalidObjectException expected) { + } + } + + + public static Object hasStaticInitializerObject; + + public static class HasStaticInitializer implements Serializable { + static { + SerializationTest.hasStaticInitializerObject = new Object(); + } + } + + public void testDeserializeStaticInitializerIsRunEventually() throws Exception { + // This was created by serializing a HasStaticInitializer + // String s = SerializationTester.serializeHex(new HasStaticInitializer()); + final String s = "aced0005737200366c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6" + + "e5465737424486173537461746963496e697469616c697a6572138aa8ed9e9b660a0200007870"; + + // Confirm the ClassLoader behaves as it should. + Class.forName( + HasStaticInitializer.class.getName(), + false /* shouldInitialize */, + Thread.currentThread().getContextClassLoader()); + assertNull(hasStaticInitializerObject); + + SerializationTester.deserializeHex(s); + + assertNotNull(hasStaticInitializerObject); + } + + @SuppressWarnings("unused") // Required for deserialization test + public static /*interface*/ class WasInterface { + } + + @SuppressWarnings("unused") // Required for deserialization test + public static class SerializableInvocationHandler implements InvocationHandler, Serializable { + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + return null; + } + } + + public void testDeserializeProxyWasInterface() throws Exception { + // This was created by serializing a proxy referencing WasInterface when it was an + // interface. + // Object o = Proxy.newProxyInstance( + // Thread.currentThread().getContextClassLoader(), + // new Class[] { WasInterface.class }, + // new SerializableInvocationHandler()); + // String s = SerializationTester.serializeHex(o); + final String s = "aced0005737d00000001002e6c6962636f72652e6a6176612e696f2e53657269616c697a6" + + "174696f6e5465737424576173496e74657266616365787200176a6176612e6c616e672e7265666c6" + + "563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f72656" + + "66c6563742f496e766f636174696f6e48616e646c65723b78707372003f6c6962636f72652e6a617" + + "6612e696f2e53657269616c697a6174696f6e546573742453657269616c697a61626c65496e766f6" + + "36174696f6e48616e646c6572e6ceffa2941ee3210200007870"; + try { + SerializationTester.deserializeHex(s); + fail(); + } catch (ClassNotFoundException expected) { + } + } + + @SuppressWarnings("unused") // Required for deserialization test + public static class WasSerializableInvocationHandler + implements InvocationHandler /*, Serializable*/ { + static final long serialVersionUID = 0L; + + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + return null; + } + } + + public void testDeserializeProxyInvocationHandlerWasSerializable() throws Exception { + // This was created by serializing a proxy referencing WasSerializableInvocationHandler when + // it was Serializable. + // Object o = Proxy.newProxyInstance( + // Thread.currentThread().getContextClassLoader(), + // new Class[] { Comparable.class }, + // new WasSerializableInvocationHandler()); + // String s = SerializationTester.serializeHex(o); + final String s = "aced0005737d0000000100146a6176612e6c616e672e436f6d70617261626c65787200176" + + "a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c00016874002" + + "54c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707" + + "37200426c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e546573742457617" + + "353657269616c697a61626c65496e766f636174696f6e48616e646c6572000000000000000002000" + + "07870"; + try { + SerializationTester.deserializeHex(s); + fail(); + } catch (InvalidClassException expected) { + } + } } diff --git a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java index e7fdb1f..eb0e3d0 100644 --- a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java +++ b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java @@ -56,63 +56,28 @@ import junit.framework.TestCase; public class KeyPairGeneratorTest extends TestCase { public void test_providerCount() { - Provider[] providers = Security.getProviders(); - // We expect there to be at least one provider. - assertTrue(providers.length > 0); - // If this fails remember to add _provider methods below. This test is sharded because it - // takes a long time to execute. - assertTrue(providers.length < 10); + // If this fails remember to add/remove _provider methods below. This test is sharded + // because it takes so long. + assertEquals(4, Security.getProviders().length); } public void test_getInstance_provider0() throws Exception { - test_getInstance(0); + test_getInstance(Security.getProviders()[0]); } public void test_getInstance_provider1() throws Exception { - test_getInstance(1); + test_getInstance(Security.getProviders()[1]); } public void test_getInstance_provider2() throws Exception { - test_getInstance(2); + test_getInstance(Security.getProviders()[2]); } public void test_getInstance_provider3() throws Exception { - test_getInstance(3); - } - - public void test_getInstance_provider4() throws Exception { - test_getInstance(4); - } - - public void test_getInstance_provider5() throws Exception { - test_getInstance(5); - } - - public void test_getInstance_provider6() throws Exception { - test_getInstance(6); - } - - public void test_getInstance_provider7() throws Exception { - test_getInstance(7); - } - - public void test_getInstance_provider8() throws Exception { - test_getInstance(8); - } - - public void test_getInstance_provider9() throws Exception { - test_getInstance(9); + test_getInstance(Security.getProviders()[3]); } - private void test_getInstance(int providerIndex) throws Exception { - Provider[] providers = Security.getProviders(); - if (providerIndex >= providers.length) { - // Providers can be added by vendors and other tests. We do not - // specify a fixed number and silenty pass if the provider at the - // specified index does not exist. - return; - } - Provider provider = providers[providerIndex]; + private void test_getInstance(Provider provider) throws Exception { Set<Provider.Service> services = provider.getServices(); for (Provider.Service service : services) { String type = service.getType(); diff --git a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java index 0b194f5..e5a6cd8 100644 --- a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java +++ b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java @@ -122,6 +122,7 @@ public class StrictJarFileTest extends TestCase { jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); if ("Test.class".equals(zipEntry.getName())) { assertNotNull(jarFile.getCertificates(zipEntry)); + assertNotNull(jarFile.getCertificateChains(zipEntry)); } } } diff --git a/luni/src/test/java/libcore/javax/net/ssl/DistinguishedNameParserTest.java b/luni/src/test/java/libcore/javax/net/ssl/DistinguishedNameParserTest.java index 723c697..91c596f 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/DistinguishedNameParserTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/DistinguishedNameParserTest.java @@ -19,38 +19,50 @@ package libcore.javax.net.ssl; import javax.net.ssl.DistinguishedNameParser; import javax.security.auth.x500.X500Principal; import junit.framework.TestCase; +import java.util.Arrays; public final class DistinguishedNameParserTest extends TestCase { - public void testGetFirstCn() { - assertFirstCn("", null); - assertFirstCn("ou=xxx", null); - assertFirstCn("ou=xxx,cn=xxx", "xxx"); - assertFirstCn("ou=xxx+cn=yyy,cn=zzz+cn=abc", "yyy"); - assertFirstCn("cn=a,cn=b", "a"); - assertFirstCn("cn=Cc,cn=Bb,cn=Aa", "Cc"); - assertFirstCn("cn=imap.gmail.com", "imap.gmail.com"); + public void testGetCns() { + assertCns(""); + assertCns("ou=xxx"); + assertCns("ou=xxx,cn=xxx", "xxx"); + assertCns("ou=xxx+cn=yyy,cn=zzz+cn=abc", "yyy", "zzz", "abc"); + assertCns("cn=a,cn=b", "a", "b"); + assertCns("cn=Cc,cn=Bb,cn=Aa", "Cc", "Bb", "Aa"); + assertCns("cn=imap.gmail.com", "imap.gmail.com"); + assertCns("l=\"abcn=a,b\", cn=c", "c"); } - public void testGetFirstCnWithOid() { - assertFirstCn("2.5.4.3=a,ou=xxx", "a"); + public void testGetCnsWithOid() { + assertCns("2.5.4.3=a,ou=xxx", "a"); } - public void testGetFirstCnWithQuotedStrings() { - assertFirstCn("cn=\"\\\" a ,=<>#;\"", "\" a ,=<>#;"); - assertFirstCn("cn=abc\\,def", "abc,def"); + public void testGetCnsWithQuotedStrings() { + assertCns("cn=\"\\\" a ,=<>#;\"", "\" a ,=<>#;"); + assertCns("cn=abc\\,def", "abc,def"); } - public void testGetFirstCnWithUtf8() { - assertFirstCn("cn=Lu\\C4\\8Di\\C4\\87", "\u004c\u0075\u010d\u0069\u0107"); + public void testGetCnsWithUtf8() { + assertCns("cn=Lu\\C4\\8Di\\C4\\87", "\u004c\u0075\u010d\u0069\u0107"); } - public void testGetFirstCnWithWhitespace() { - assertFirstCn("ou=a, cn= a b ,o=x", "a b"); - assertFirstCn("cn=\" a b \" ,o=x", " a b "); + public void testGetCnsWithWhitespace() { + assertCns("ou=a, cn= a b ,o=x", "a b"); + assertCns("cn=\" a b \" ,o=x", " a b "); } - private void assertFirstCn(String dn, String expected) { + private void assertCns(String dn, String... expected) { X500Principal principal = new X500Principal(dn); - assertEquals(dn, expected, new DistinguishedNameParser(principal).findMostSpecific("cn")); + DistinguishedNameParser parser = new DistinguishedNameParser(principal); + + // Test getAllMostSpecificFirst + assertEquals(dn, Arrays.asList(expected), parser.getAllMostSpecificFirst("cn")); + + // Test findMostSpecific + if (expected.length > 0) { + assertEquals(dn, expected[0], parser.findMostSpecific("cn")); + } else { + assertNull(dn, parser.findMostSpecific("cn")); + } } } diff --git a/support/src/test/java/tests/resources/hyts_signed_ambiguousSignerArray.jar b/support/src/test/java/tests/resources/hyts_signed_ambiguousSignerArray.jar Binary files differnew file mode 100644 index 0000000..7da4b59 --- /dev/null +++ b/support/src/test/java/tests/resources/hyts_signed_ambiguousSignerArray.jar diff --git a/support/src/test/java/tests/resources/hyts_signed_invalidChain.jar b/support/src/test/java/tests/resources/hyts_signed_invalidChain.jar Binary files differnew file mode 100644 index 0000000..23d7ae8 --- /dev/null +++ b/support/src/test/java/tests/resources/hyts_signed_invalidChain.jar diff --git a/support/src/test/java/tests/resources/hyts_signed_validChain.jar b/support/src/test/java/tests/resources/hyts_signed_validChain.jar Binary files differnew file mode 100644 index 0000000..d1f4c56 --- /dev/null +++ b/support/src/test/java/tests/resources/hyts_signed_validChain.jar |