am fb01ca4a: Merge "Fixes for URLTest & HttpCookieTest"
* commit 'fb01ca4a50387c884a217ea179c0608fd5b57c67':
Fixes for URLTest & HttpCookieTest
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 5dc939e..c44d991 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -50,18 +50,20 @@
endef
# The Java files and their associated resources.
-core_src_files := $(call all-main-java-files-under,dalvik dex dom json luni xml)
-core_src_files += $(call all-main-java-files-under,libdvm)
+common_core_src_files := $(call all-main-java-files-under,dalvik dex dom json luni xml)
core_resource_dirs := $(call all-core-resource-dirs,main)
test_resource_dirs := $(call all-core-resource-dirs,test)
ifeq ($(EMMA_INSTRUMENT),true)
ifneq ($(EMMA_INSTRUMENT_STATIC),true)
- core_src_files += $(call all-java-files-under, ../external/emma/core ../external/emma/pregenerated)
+ common_core_src_files += $(call all-java-files-under, ../external/emma/core ../external/emma/pregenerated)
core_resource_dirs += ../external/emma/core/res ../external/emma/pregenerated/res
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
#local_javac_flags+=-Xlint:all -Xlint:-serial,-deprecation,-unchecked
local_javac_flags+=-Xmaxwarns 9999999
@@ -73,23 +75,28 @@
# Definitions to make the core library.
include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(core_src_files)
+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)
-core-intermediates := ${intermediates}
-
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libart_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-libart
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+LOCAL_REQUIRED_MODULES := tzdata
+include $(BUILD_JAVA_LIBRARY)
ifeq ($(LIBCORE_SKIP_TESTS),)
# Make the core-tests library.
@@ -159,23 +166,30 @@
ifeq ($(WITH_HOST_DALVIK),true)
# Definitions to make the core library.
-
include $(CLEAR_VARS)
-
- LOCAL_SRC_FILES := $(core_src_files)
+ 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_BUILD_HOST_DEX := true
-
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := core-hostdex
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
LOCAL_REQUIRED_MODULES := tzdata-host
+ include $(BUILD_HOST_JAVA_LIBRARY)
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(libart_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_BUILD_HOST_DEX := true
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := core-libart-hostdex
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+ LOCAL_REQUIRED_MODULES := tzdata-host
include $(BUILD_HOST_JAVA_LIBRARY)
# Make the core-tests library.
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 04972f1..f3bee10 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -147,17 +147,13 @@
* do not refer to existing and readable directories.
*/
private static File[] splitLibraryPath(String path) {
- /*
- * Native libraries may exist in both the system and
- * application library paths, and we use this search order:
- *
- * 1. this class loader's library path for application
- * libraries
- * 2. the VM's library path from the system
- * property for system libraries
- *
- * This order was reversed prior to Gingerbread; see http://b/2933456.
- */
+ // Native libraries may exist in both the system and
+ // application library paths, and we use this search order:
+ //
+ // 1. this class loader's library path for application libraries
+ // 2. the VM's library path from the system property for system libraries
+ //
+ // This order was reversed prior to Gingerbread; see http://b/2933456.
ArrayList<File> result = splitPaths(path, System.getProperty("java.library.path"), true);
return result.toArray(new File[result.size()]);
}
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index 5d59838..a46119f 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -243,7 +243,7 @@
/**
* This method exists for binary compatibility. It was part of
- * the allocation limits API which was removed in Honeycomb.
+ * the allocation limits API which was removed in Android 3.0 (Honeycomb).
*/
@Deprecated
public static int setAllocationLimit(int limit) {
@@ -252,7 +252,7 @@
/**
* This method exists for binary compatibility. It was part of
- * the allocation limits API which was removed in Honeycomb.
+ * the allocation limits API which was removed in Android 3.0 (Honeycomb).
*/
@Deprecated
public static int setGlobalAllocationLimit(int limit) {
@@ -364,4 +364,25 @@
* @return the number of matching instances.
*/
public static native long countInstancesOfClass(Class klass, boolean assignable);
+
+ /**
+ * Export the heap per-space stats for dumpsys meminfo.
+ *
+ * The content of the array is:
+ *
+ * <pre>
+ * data[0] : the application heap space size
+ * data[1] : the application heap space allocated bytes
+ * data[2] : the application heap space free bytes
+ * data[3] : the zygote heap space size
+ * data[4] : the zygote heap space allocated size
+ * data[5] : the zygote heap space free size
+ * data[6] : the large object space size
+ * data[7] : the large object space allocated bytes
+ * data[8] : the large object space free bytes
+ * </pre>
+ *
+ * @param data the array into which the stats are written.
+ */
+ public static native void getHeapSpaceStats(long[] data);
}
diff --git a/dex/src/main/java/com/android/dex/Dex.java b/dex/src/main/java/com/android/dex/Dex.java
index 29cd30e..116a33c 100644
--- a/dex/src/main/java/com/android/dex/Dex.java
+++ b/dex/src/main/java/com/android/dex/Dex.java
@@ -53,78 +53,19 @@
private static final int CHECKSUM_SIZE = 4;
private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
private static final int SIGNATURE_SIZE = 20;
+ // Provided as a convenience to avoid a memory allocation to benefit Dalvik.
+ // Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik.
+ static final short[] EMPTY_SHORT_ARRAY = new short[0];
private ByteBuffer data;
private final TableOfContents tableOfContents = new TableOfContents();
private int nextSectionStart = 0;
-
- private static abstract class AbstractRandomAccessList<T>
- extends AbstractList<T> implements RandomAccess {
- }
-
- private List<String> strings = new AbstractRandomAccessList<String>() {
- @Override public String get(int index) {
- checkBounds(index, tableOfContents.stringIds.size);
- return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
- .readString();
- }
- @Override public int size() {
- return tableOfContents.stringIds.size;
- }
- };
-
- private final List<Integer> typeIds = new AbstractRandomAccessList<Integer>() {
- @Override public Integer get(int index) {
- checkBounds(index, tableOfContents.typeIds.size);
- return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();
- }
- @Override public int size() {
- return tableOfContents.typeIds.size;
- }
- };
-
- private final List<String> typeNames = new AbstractRandomAccessList<String>() {
- @Override public String get(int index) {
- checkBounds(index, tableOfContents.typeIds.size);
- return strings.get(typeIds.get(index));
- }
- @Override public int size() {
- return tableOfContents.typeIds.size;
- }
- };
-
- private final List<ProtoId> protoIds = new AbstractRandomAccessList<ProtoId>() {
- @Override public ProtoId get(int index) {
- checkBounds(index, tableOfContents.protoIds.size);
- return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
- .readProtoId();
- }
- @Override public int size() {
- return tableOfContents.protoIds.size;
- }
- };
-
- private final List<FieldId> fieldIds = new AbstractRandomAccessList<FieldId>() {
- @Override public FieldId get(int index) {
- checkBounds(index, tableOfContents.fieldIds.size);
- return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
- .readFieldId();
- }
- @Override public int size() {
- return tableOfContents.fieldIds.size;
- }
- };
-
- private final List<MethodId> methodIds = new AbstractRandomAccessList<MethodId>() {
- @Override public MethodId get(int index) {
- checkBounds(index, tableOfContents.methodIds.size);
- return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
- .readMethodId();
- }
- @Override public int size() {
- return tableOfContents.methodIds.size;
- }
- };
+ private final StringTable strings = new StringTable();
+ private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();
+ private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();
+ private final ProtoIdTable protoIds = new ProtoIdTable();
+ private final FieldIdTable fieldIds = new FieldIdTable();
+ private final MethodIdTable methodIds = new MethodIdTable();
/**
* Creates a new dex that reads from {@code data}. It is an error to modify
@@ -312,31 +253,7 @@
}
public Iterable<ClassDef> classDefs() {
- return new Iterable<ClassDef>() {
- public Iterator<ClassDef> iterator() {
- if (!tableOfContents.classDefs.exists()) {
- return Collections.<ClassDef>emptySet().iterator();
- }
- return new Iterator<ClassDef>() {
- private Dex.Section in = open(tableOfContents.classDefs.off);
- private int count = 0;
-
- public boolean hasNext() {
- return count < tableOfContents.classDefs.size;
- }
- public ClassDef next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- count++;
- return in.readClassDef();
- }
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
- };
+ return new ClassDefIterable();
}
public TypeList readTypeList(int offset) {
@@ -412,6 +329,187 @@
open(CHECKSUM_OFFSET).writeInt(computeChecksum());
}
+ /**
+ * Look up a field id name index from a field index. Cheaper than:
+ * {@code fieldIds().get(fieldDexIndex).getNameIndex();}
+ */
+ public int nameIndexFromFieldIndex(int fieldIndex) {
+ checkBounds(fieldIndex, tableOfContents.fieldIds.size);
+ int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
+ position += SizeOf.USHORT; // declaringClassIndex
+ position += SizeOf.USHORT; // typeIndex
+ return data.getInt(position); // nameIndex
+ }
+
+ public int findStringIndex(String s) {
+ return Collections.binarySearch(strings, s);
+ }
+
+ public int findTypeIndex(String descriptor) {
+ return Collections.binarySearch(typeNames, descriptor);
+ }
+
+ public int findFieldIndex(FieldId fieldId) {
+ return Collections.binarySearch(fieldIds, fieldId);
+ }
+
+ public int findMethodIndex(MethodId methodId) {
+ return Collections.binarySearch(methodIds, methodId);
+ }
+
+ public int findClassDefIndexFromTypeIndex(int typeIndex) {
+ checkBounds(typeIndex, tableOfContents.typeIds.size);
+ if (!tableOfContents.classDefs.exists()) {
+ return -1;
+ }
+ for (int i = 0; i < tableOfContents.classDefs.size; i++) {
+ if (typeIndexFromClassDefIndex(i) == typeIndex) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Look up a field id type index from a field index. Cheaper than:
+ * {@code fieldIds().get(fieldDexIndex).getTypeIndex();}
+ */
+ public int typeIndexFromFieldIndex(int fieldIndex) {
+ checkBounds(fieldIndex, tableOfContents.fieldIds.size);
+ int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
+ position += SizeOf.USHORT; // declaringClassIndex
+ return data.getShort(position) & 0xFFFF; // typeIndex
+ }
+
+ /**
+ * Look up a method id declaring class index from a method index. Cheaper than:
+ * {@code methodIds().get(methodIndex).getDeclaringClassIndex();}
+ */
+ public int declaringClassIndexFromMethodIndex(int methodIndex) {
+ checkBounds(methodIndex, tableOfContents.methodIds.size);
+ int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+ return data.getShort(position) & 0xFFFF; // declaringClassIndex
+ }
+
+ /**
+ * Look up a method id name index from a method index. Cheaper than:
+ * {@code methodIds().get(methodIndex).getNameIndex();}
+ */
+ public int nameIndexFromMethodIndex(int methodIndex) {
+ checkBounds(methodIndex, tableOfContents.methodIds.size);
+ int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+ position += SizeOf.USHORT; // declaringClassIndex
+ position += SizeOf.USHORT; // protoIndex
+ return data.getInt(position); // nameIndex
+ }
+
+ /**
+ * Look up a parameter type ids from a method index. Cheaper than:
+ * {@code readTypeList(protoIds.get(methodIds().get(methodDexIndex).getProtoIndex()).getParametersOffset()).getTypes();}
+ */
+ public short[] parameterTypeIndicesFromMethodIndex(int methodIndex) {
+ checkBounds(methodIndex, tableOfContents.methodIds.size);
+ int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+ position += SizeOf.USHORT; // declaringClassIndex
+ int protoIndex = data.getShort(position) & 0xFFFF;
+ checkBounds(protoIndex, tableOfContents.protoIds.size);
+ position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
+ position += SizeOf.UINT; // shortyIndex
+ position += SizeOf.UINT; // returnTypeIndex
+ int parametersOffset = data.getInt(position);
+ if (parametersOffset == 0) {
+ return EMPTY_SHORT_ARRAY;
+ }
+ position = parametersOffset;
+ int size = data.getInt(position);
+ if (size <= 0) {
+ throw new AssertionError("Unexpected parameter type list size: " + size);
+ }
+ position += SizeOf.UINT;
+ short[] types = new short[size];
+ for (int i = 0; i < size; i++) {
+ types[i] = data.getShort(position);
+ position += SizeOf.USHORT;
+ }
+ return types;
+ }
+
+ /**
+ * Look up a method id return type index from a method index. Cheaper than:
+ * {@code protoIds().get(methodIds().get(methodDexIndex).getProtoIndex()).getReturnTypeIndex();}
+ */
+ public int returnTypeIndexFromMethodIndex(int methodIndex) {
+ checkBounds(methodIndex, tableOfContents.methodIds.size);
+ int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
+ position += SizeOf.USHORT; // declaringClassIndex
+ int protoIndex = data.getShort(position) & 0xFFFF;
+ checkBounds(protoIndex, tableOfContents.protoIds.size);
+ position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
+ position += SizeOf.UINT; // shortyIndex
+ return data.getInt(position); // returnTypeIndex
+ }
+
+ /**
+ * Look up a descriptor index from a type index. Cheaper than:
+ * {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
+ */
+ public int descriptorIndexFromTypeIndex(int typeIndex) {
+ checkBounds(typeIndex, tableOfContents.typeIds.size);
+ int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
+ return data.getInt(position);
+ }
+
+ /**
+ * Look up a type index index from a class def index.
+ */
+ public int typeIndexFromClassDefIndex(int classDefIndex) {
+ checkBounds(classDefIndex, tableOfContents.classDefs.size);
+ int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
+ return data.getInt(position);
+ }
+
+ /**
+ * Look up a type index index from a class def index.
+ */
+ public int annotationDirectoryOffsetFromClassDefIndex(int classDefIndex) {
+ checkBounds(classDefIndex, tableOfContents.classDefs.size);
+ int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
+ position += SizeOf.UINT; // type
+ position += SizeOf.UINT; // accessFlags
+ position += SizeOf.UINT; // superType
+ position += SizeOf.UINT; // interfacesOffset
+ position += SizeOf.UINT; // sourceFileIndex
+ return data.getInt(position);
+ }
+
+ /**
+ * Look up interface types indices from a return type index from a method index. Cheaper than:
+ * {@code ...getClassDef(classDefIndex).getInterfaces();}
+ */
+ public short[] interfaceTypeIndicesFromClassDefIndex(int classDefIndex) {
+ checkBounds(classDefIndex, tableOfContents.classDefs.size);
+ int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
+ position += SizeOf.UINT; // type
+ position += SizeOf.UINT; // accessFlags
+ position += SizeOf.UINT; // superType
+ int interfacesOffset = data.getInt(position);
+ if (interfacesOffset == 0) {
+ return EMPTY_SHORT_ARRAY;
+ }
+ position = interfacesOffset;
+ int size = data.getInt(position);
+ if (size <= 0) {
+ throw new AssertionError("Unexpected interfaces list size: " + size);
+ }
+ position += SizeOf.UINT;
+ short[] types = new short[size];
+ for (int i = 0; i < size; i++) {
+ types[i] = data.getShort(position);
+ position += SizeOf.USHORT;
+ }
+ return types;
+ }
+
public final class Section implements ByteInput, ByteOutput {
private final String name;
private final ByteBuffer data;
@@ -450,6 +548,9 @@
}
public short[] readShortArray(int length) {
+ if (length == 0) {
+ return EMPTY_SHORT_ARRAY;
+ }
short[] result = new short[length];
for (int i = 0; i < length; i++) {
result[i] = readShort();
@@ -475,10 +576,7 @@
public TypeList readTypeList() {
int size = readInt();
- short[] types = new short[size];
- for (int i = 0; i < size; i++) {
- types[i] = readShort();
- }
+ short[] types = readShortArray(size);
alignToFourBytes();
return new TypeList(Dex.this, types);
}
@@ -552,71 +650,71 @@
Try[] tries;
CatchHandler[] catchHandlers;
if (triesSize > 0) {
- if (instructions.length % 2 == 1) {
- readShort(); // padding
- }
+ if (instructions.length % 2 == 1) {
+ readShort(); // padding
+ }
- /*
- * We can't read the tries until we've read the catch handlers.
- * Unfortunately they're in the opposite order in the dex file
- * so we need to read them out-of-order.
- */
- Section triesSection = open(data.position());
- skip(triesSize * SizeOf.TRY_ITEM);
- catchHandlers = readCatchHandlers();
- tries = triesSection.readTries(triesSize, catchHandlers);
- } else {
- tries = new Try[0];
- catchHandlers = new CatchHandler[0];
- }
- return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
- tries, catchHandlers);
+ /*
+ * We can't read the tries until we've read the catch handlers.
+ * Unfortunately they're in the opposite order in the dex file
+ * so we need to read them out-of-order.
+ */
+ Section triesSection = open(data.position());
+ skip(triesSize * SizeOf.TRY_ITEM);
+ catchHandlers = readCatchHandlers();
+ tries = triesSection.readTries(triesSize, catchHandlers);
+ } else {
+ tries = new Try[0];
+ catchHandlers = new CatchHandler[0];
+ }
+ return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
+ tries, catchHandlers);
}
private CatchHandler[] readCatchHandlers() {
- int baseOffset = data.position();
- int catchHandlersSize = readUleb128();
- CatchHandler[] result = new CatchHandler[catchHandlersSize];
- for (int i = 0; i < catchHandlersSize; i++) {
- int offset = data.position() - baseOffset;
- result[i] = readCatchHandler(offset);
- }
- return result;
+ int baseOffset = data.position();
+ int catchHandlersSize = readUleb128();
+ CatchHandler[] result = new CatchHandler[catchHandlersSize];
+ for (int i = 0; i < catchHandlersSize; i++) {
+ int offset = data.position() - baseOffset;
+ result[i] = readCatchHandler(offset);
+ }
+ return result;
}
private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
- Try[] result = new Try[triesSize];
- for (int i = 0; i < triesSize; i++) {
- int startAddress = readInt();
- int instructionCount = readUnsignedShort();
- int handlerOffset = readUnsignedShort();
- int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
- result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
- }
- return result;
+ Try[] result = new Try[triesSize];
+ for (int i = 0; i < triesSize; i++) {
+ int startAddress = readInt();
+ int instructionCount = readUnsignedShort();
+ int handlerOffset = readUnsignedShort();
+ int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
+ result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
+ }
+ return result;
}
private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
- for (int i = 0; i < catchHandlers.length; i++) {
- CatchHandler catchHandler = catchHandlers[i];
- if (catchHandler.getOffset() == offset) {
- return i;
+ for (int i = 0; i < catchHandlers.length; i++) {
+ CatchHandler catchHandler = catchHandlers[i];
+ if (catchHandler.getOffset() == offset) {
+ return i;
+ }
}
- }
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException();
}
private CatchHandler readCatchHandler(int offset) {
- int size = readSleb128();
- int handlersCount = Math.abs(size);
- int[] typeIndexes = new int[handlersCount];
- int[] addresses = new int[handlersCount];
- for (int i = 0; i < handlersCount; i++) {
- typeIndexes[i] = readUleb128();
- addresses[i] = readUleb128();
- }
- int catchAllAddress = size <= 0 ? readUleb128() : -1;
- return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
+ int size = readSleb128();
+ int handlersCount = Math.abs(size);
+ int[] typeIndexes = new int[handlersCount];
+ int[] addresses = new int[handlersCount];
+ for (int i = 0; i < handlersCount; i++) {
+ typeIndexes[i] = readUleb128();
+ addresses[i] = readUleb128();
+ }
+ int catchAllAddress = size <= 0 ? readUleb128() : -1;
+ return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
}
private ClassData readClassData() {
@@ -680,11 +778,11 @@
}
public void skip(int count) {
- if (count < 0) {
- throw new IllegalArgumentException();
- }
- data.position(data.position() + count);
- }
+ if (count < 0) {
+ throw new IllegalArgumentException();
+ }
+ data.position(data.position() + count);
+ }
/**
* Skips bytes until the position is aligned to a multiple of 4.
@@ -788,4 +886,98 @@
return data.position() - initialPosition;
}
}
+
+ private final class StringTable extends AbstractList<String> implements RandomAccess {
+ @Override public String get(int index) {
+ checkBounds(index, tableOfContents.stringIds.size);
+ return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
+ .readString();
+ }
+ @Override public int size() {
+ return tableOfContents.stringIds.size;
+ }
+ };
+
+ private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
+ implements RandomAccess {
+ @Override public Integer get(int index) {
+ return descriptorIndexFromTypeIndex(index);
+ }
+ @Override public int size() {
+ return tableOfContents.typeIds.size;
+ }
+ };
+
+ private final class TypeIndexToDescriptorTable extends AbstractList<String>
+ implements RandomAccess {
+ @Override public String get(int index) {
+ return strings.get(descriptorIndexFromTypeIndex(index));
+ }
+ @Override public int size() {
+ return tableOfContents.typeIds.size;
+ }
+ };
+
+ private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
+ @Override public ProtoId get(int index) {
+ checkBounds(index, tableOfContents.protoIds.size);
+ return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
+ .readProtoId();
+ }
+ @Override public int size() {
+ return tableOfContents.protoIds.size;
+ }
+ };
+
+ private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
+ @Override public FieldId get(int index) {
+ checkBounds(index, tableOfContents.fieldIds.size);
+ return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+ .readFieldId();
+ }
+ @Override public int size() {
+ return tableOfContents.fieldIds.size;
+ }
+ };
+
+ private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
+ @Override public MethodId get(int index) {
+ checkBounds(index, tableOfContents.methodIds.size);
+ return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+ .readMethodId();
+ }
+ @Override public int size() {
+ return tableOfContents.methodIds.size;
+ }
+ };
+
+ private final class ClassDefIterator implements Iterator<ClassDef> {
+ private final Dex.Section in = open(tableOfContents.classDefs.off);
+ private int count = 0;
+
+ @Override
+ public boolean hasNext() {
+ return count < tableOfContents.classDefs.size;
+ }
+ @Override
+ public ClassDef next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ count++;
+ return in.readClassDef();
+ }
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ private final class ClassDefIterable implements Iterable<ClassDef> {
+ public Iterator<ClassDef> iterator() {
+ return !tableOfContents.classDefs.exists()
+ ? Collections.<ClassDef>emptySet().iterator()
+ : new ClassDefIterator();
+ }
+ };
}
diff --git a/dex/src/main/java/com/android/dex/DexFormat.java b/dex/src/main/java/com/android/dex/DexFormat.java
index 85941fd..9319bc2 100644
--- a/dex/src/main/java/com/android/dex/DexFormat.java
+++ b/dex/src/main/java/com/android/dex/DexFormat.java
@@ -56,6 +56,19 @@
public static final int ENDIAN_TAG = 0x12345678;
/**
+ * Maximum addressable field or method index.
+ * The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or
+ * meth@CCCC.
+ */
+ public static final int MAX_MEMBER_IDX = 0xFFFF;
+
+ /**
+ * Maximum addressable type index.
+ * The largest addressable type is 0xffff, in the "instruction formats" spec as type@CCCC.
+ */
+ public static final int MAX_TYPE_IDX = 0xFFFF;
+
+ /**
* Returns the API level corresponding to the given magic number,
* or {@code -1} if the given array is not a well-formed dex file
* magic number.
diff --git a/dex/src/main/java/com/android/dex/TypeList.java b/dex/src/main/java/com/android/dex/TypeList.java
index 6e321fb..123e82c 100644
--- a/dex/src/main/java/com/android/dex/TypeList.java
+++ b/dex/src/main/java/com/android/dex/TypeList.java
@@ -20,7 +20,7 @@
public final class TypeList implements Comparable<TypeList> {
- public static final TypeList EMPTY = new TypeList(null, new short[0]);
+ public static final TypeList EMPTY = new TypeList(null, Dex.EMPTY_SHORT_ARRAY);
private final Dex dex;
private final short[] types;
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
new file mode 100644
index 0000000..8f19e3a
--- /dev/null
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -0,0 +1,247 @@
+/*
+ * 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 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();
+
+ /**
+ * 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();
+
+ /**
+ * 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).
+ */
+ public native void setTargetSdkVersion(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 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);
+
+ public native void trimHeap();
+ public native void concurrentGC();
+
+ public void preloadDexCaches() {
+ // Do nothing with ART, image generation already does this.
+ }
+}
diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java
new file mode 100644
index 0000000..ee0a0db
--- /dev/null
+++ b/libart/src/main/java/dalvik/system/VMStack.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ *
+ * @return the requested class, or {@code null}.
+ */
+ public static Class<?> getStackClass1() {
+ return getStackClass2();
+ }
+
+ /**
+ * Returns the class of the caller's caller's caller.
+ *
+ * @return the requested class, or {@code null}.
+ */
+ native public static Class<?> getStackClass2();
+
+ /**
+ * Returns the first ClassLoader on the call stack that isn't either of
+ * the passed-in ClassLoaders.
+ */
+ public native static ClassLoader getClosestUserClassLoader(ClassLoader bootstrap,
+ ClassLoader system);
+
+ /**
+ * 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/libart/src/main/java/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java
new file mode 100644
index 0000000..d1b9a18
--- /dev/null
+++ b/libart/src/main/java/java/lang/Class.java
@@ -0,0 +1,1756 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2006-2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+import com.android.dex.Dex;
+import dalvik.system.VMStack;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AbstractMethod;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.ArtField;
+import java.lang.reflect.ArtMethod;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+import libcore.reflect.GenericSignatureParser;
+import libcore.reflect.InternalNames;
+import libcore.reflect.Types;
+import libcore.util.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>descriptor</em>, which is the letter "L", followed by the
+ * class name and a semicolon (";"). The descriptor is what the runtime system
+ * uses internally for identifying the class (for example in a DEX file).
+ * </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 descriptors 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 descriptor of the class representing the leaf component type,
+ * which can be either an object type or a primitive type. The descriptor of a
+ * {@code Class} representing an array type is the same as its name. Examples
+ * of array class descriptors are:
+ * </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;
+
+ /** defining class loader, or NULL for the "bootstrap" system loader. */
+ private transient ClassLoader classLoader;
+
+ /**
+ * For array classes, the component class object for instanceof/checkcast (for String[][][],
+ * this will be String[][]). NULL for non-array classes.
+ */
+ private transient Class<?> componentType;
+ /**
+ * DexCache of resolved constant pool entries. Will be null for certain VM-generated classes
+ * e.g. arrays and primitive classes.
+ */
+ private transient DexCache dexCache;
+
+ /** static, private, and <init> methods. */
+ private transient ArtMethod[] directMethods;
+
+ /**
+ * Instance fields. These describe the layout of the contents of an Object. Note that only the
+ * fields directly declared by this class are listed in iFields; fields declared by a
+ * superclass are listed in the superclass's Class.iFields.
+ *
+ * All instance fields that refer to objects are guaranteed to be at the beginning of the field
+ * list. {@link Class#numReferenceInstanceFields} specifies the number of reference fields.
+ */
+ private transient ArtField[] iFields;
+
+ /**
+ * The interface table (iftable_) contains pairs of a interface class and an array of the
+ * interface methods. There is one pair per interface supported by this class. That
+ * means one pair for each interface we support directly, indirectly via superclass, or
+ * indirectly via a superinterface. This will be null if neither we nor our superclass
+ * implement any interfaces.
+ *
+ * Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()".
+ * Invoke faceObj.blah(), where "blah" is part of the Face interface. We can't easily use a
+ * single vtable.
+ *
+ * For every interface a concrete class implements, we create an array of the concrete vtable_
+ * methods for the methods in the interface.
+ */
+ private transient Object[] ifTable;
+
+ /** Lazily computed name of this class; always prefer calling getName(). */
+ private transient String name;
+
+ /** Static fields */
+ private transient ArtField[] sFields;
+
+ /** The superclass, or NULL if this is java.lang.Object, an interface or primitive type. */
+ private transient Class<? super T> superClass;
+
+ /** If class verify fails, we must return same error on subsequent tries. */
+ private transient Class<?> verifyErrorClass;
+
+ /** Virtual methods defined in this class; invoked through vtable. */
+ private transient ArtMethod[] virtualMethods;
+
+ /**
+ * Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass
+ * is copied in, and virtual methods from our class either replace those from the super or are
+ * appended. For abstract classes, methods may be created in the vtable that aren't in
+ * virtual_ methods_ for miranda methods.
+ */
+ private transient ArtMethod[] vtable;
+
+ /** access flags; low 16 bits are defined by VM spec */
+ private transient int accessFlags;
+
+ /**
+ * Total size of the Class instance; used when allocating storage on GC heap.
+ * See also {@link Class#objectSize}.
+ */
+ private transient int classSize;
+
+ /**
+ * tid used to check for recursive static initializer invocation.
+ */
+ private transient int clinitThreadId;
+
+ /**
+ * Class def index from dex file. An index of 65535 indicates that there is no class definition,
+ * for example for an array type.
+ * TODO: really 16bits as type indices are 16bit.
+ */
+ private transient int dexClassDefIndex;
+
+ /**
+ * Class type index from dex file, lazily computed. An index of 65535 indicates that the type
+ * index isn't known. Volatile to avoid double-checked locking bugs.
+ * TODO: really 16bits as type indices are 16bit.
+ */
+ private transient volatile int dexTypeIndex;
+
+ /** Number of instance fields that are object references. */
+ private transient int numReferenceInstanceFields;
+
+ /** Number of static fields that are object references. */
+ private transient int numReferenceStaticFields;
+
+ /**
+ * Total object size; used when allocating storage on GC heap. For interfaces and abstract
+ * classes this will be zero. See also {@link Class#classSize}.
+ */
+ private transient int objectSize;
+
+ /** Primitive type value, or 0 if not a primitive type; set for generated primitive classes. */
+ private transient int primitiveType;
+
+ /** Bitmap of offsets of iFields. */
+ private transient int referenceInstanceOffsets;
+
+ /** Bitmap of offsets of sFields. */
+ private transient int referenceStaticOffsets;
+
+ /** State of class initialization */
+ private transient int status;
+
+ private Class() {
+ // Prevent this class to be instantiated, instance should be created by JVM only
+ }
+
+ /**
+ * Returns a {@code Class} object which represents the class with
+ * the given name. The name should be the name of a non-primitive
+ * class, as described in the {@link Class class definition}.
+ * Primitive types can not be found using this method; use {@code
+ * int.class} or {@code Integer.TYPE} instead.
+ *
+ * <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 cannot be found.
+ * @throws LinkageError
+ * if an error occurs during linkage
+ * @throws ExceptionInInitializerError
+ * if an exception occurs during static initialization of a
+ * class.
+ */
+ public static Class<?> forName(String className) throws ClassNotFoundException {
+ return forName(className, true, VMStack.getCallingClassLoader());
+ }
+
+ /**
+ * Returns a {@code Class} object which represents the class with
+ * the given name. The name should be the name of a non-primitive
+ * class, as described in the {@link Class class definition}.
+ * Primitive types can not be found using this method; use {@code
+ * int.class} or {@code Integer.TYPE} instead.
+ *
+ * <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 cannot be found.
+ * @throws LinkageError
+ * if an error occurs during linkage
+ * @throws ExceptionInInitializerError
+ * if an exception occurs during static initialization of a
+ * class.
+ */
+ public static Class<?> forName(String className, boolean shouldInitialize,
+ ClassLoader classLoader) throws ClassNotFoundException {
+
+ if (classLoader == null) {
+ classLoader = ClassLoader.getSystemClassLoader();
+ }
+ // Catch an Exception thrown by the underlying native code. It wraps
+ // up everything inside a ClassNotFoundException, even if e.g. an
+ // Error occurred during initialization. This as a workaround for
+ // an ExceptionInInitializerError that's also wrapped. It is actually
+ // expected to be thrown. Maybe the same goes for other errors.
+ // Not wrapping up all the errors will break android though.
+ Class<?> result;
+ try {
+ result = classForName(className, shouldInitialize, classLoader);
+ } catch (ClassNotFoundException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof LinkageError) {
+ throw (LinkageError) cause;
+ }
+ throw e;
+ }
+ return result;
+ }
+
+ static native Class<?> classForName(String className, boolean shouldInitialize,
+ ClassLoader classLoader) throws ClassNotFoundException;
+
+ /**
+ * Returns an array containing {@code Class} objects for all
+ * public classes, interfaces, enums and annotations that are
+ * members of this class and its superclasses. This does not
+ * include classes of implemented interfaces. If there are no
+ * such class members or if this object represents a primitive
+ * type then an array of length 0 is returned.
+ */
+ public Class<?>[] getClasses() {
+ List<Class<?>> result = new ArrayList<Class<?>>();
+ for (Class<?> c = this; c != null; c = c.superClass) {
+ for (Class<?> member : c.getDeclaredClasses()) {
+ if (Modifier.isPublic(member.getModifiers())) {
+ result.add(member);
+ }
+ }
+ }
+ return result.toArray(new Class[result.size()]);
+ }
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ return AnnotationAccess.getAnnotation(this, annotationType);
+ }
+
+ /**
+ * Returns an array containing all the annotations of this class. If there are no annotations
+ * then an empty array is returned.
+ *
+ * @see #getDeclaredAnnotations()
+ */
+ @Override public Annotation[] getAnnotations() {
+ return AnnotationAccess.getAnnotations(this);
+ }
+
+ /**
+ * Returns the canonical name of this class. If this class does not have a
+ * canonical name as defined in the Java Language Specification, then the
+ * method returns {@code null}.
+ */
+ public String getCanonicalName() {
+ if (isLocalClass() || isAnonymousClass())
+ return null;
+
+ if (isArray()) {
+ /*
+ * The canonical name of an array type depends on the (existence of)
+ * the component type's canonical name.
+ */
+ String name = getComponentType().getCanonicalName();
+ if (name != null) {
+ return name + "[]";
+ }
+ } else if (isMemberClass()) {
+ /*
+ * The canonical name of an inner class depends on the (existence
+ * of) the declaring class' canonical name.
+ */
+ String name = getDeclaringClass().getCanonicalName();
+ if (name != null) {
+ return name + "." + getSimpleName();
+ }
+ } else {
+ /*
+ * The canonical name of a top-level class or primitive type is
+ * equal to the fully qualified name.
+ */
+ return getName();
+ }
+
+ /*
+ * Other classes don't have a canonical name.
+ */
+ return null;
+ }
+
+ /**
+ * Returns the class loader which was used to load the class represented by
+ * this {@code Class}. Implementations are free to return {@code null} for
+ * classes that were loaded by the bootstrap class loader. The Android
+ * reference implementation, though, always returns a reference to an actual
+ * class loader.
+ */
+ public ClassLoader getClassLoader() {
+ if (this.isPrimitive()) {
+ return null;
+ }
+
+ ClassLoader loader = getClassLoaderImpl();
+ if (loader == null) {
+ loader = BootClassLoader.getInstance();
+ }
+ return loader;
+ }
+
+ /**
+ * This must be provided by the VM vendor, as it is used by other provided
+ * class implementations in this package. Outside of this class, it is used
+ * by SecurityManager.classLoaderDepth(),
+ * currentClassLoader() and currentLoadedClass(). Return the ClassLoader for
+ * this Class without doing any security checks. The bootstrap ClassLoader
+ * is returned, unlike getClassLoader() which returns null in place of the
+ * bootstrap ClassLoader.
+ */
+ ClassLoader getClassLoaderImpl() {
+ ClassLoader loader = classLoader;
+ return loader == null ? BootClassLoader.getInstance() : loader;
+ }
+
+ /**
+ * Returns a {@code Class} object which represents the component type if
+ * this class represents an array type. Returns {@code null} if this class
+ * does not represent an array type. The component type of an array type is
+ * the type of the elements of the array.
+ */
+ public Class<?> getComponentType() {
+ return componentType;
+ }
+
+ /**
+ * Returns the dex file from which this class was loaded.
+ *
+ * @hide
+ */
+ public Dex getDex() {
+ if (dexCache == null) {
+ return null;
+ }
+ return dexCache.getDex();
+ }
+
+ /**
+ * Returns a string from the dex cache, computing the string from the dex file if necessary.
+ *
+ * @hide
+ */
+ public String getDexCacheString(Dex dex, int dexStringIndex) {
+ String[] dexCacheStrings = dexCache.strings;
+ String s = dexCacheStrings[dexStringIndex];
+ if (s == null) {
+ s = dex.strings().get(dexStringIndex).intern();
+ dexCacheStrings[dexStringIndex] = s;
+ }
+ return s;
+ }
+
+ /**
+ * Returns a resolved type from the dex cache, computing the type from the dex file if
+ * necessary.
+ *
+ * @hide
+ */
+ public Class<?> getDexCacheType(Dex dex, int dexTypeIndex) {
+ Class<?>[] dexCacheResolvedTypes = dexCache.resolvedTypes;
+ Class<?> resolvedType = dexCacheResolvedTypes[dexTypeIndex];
+ if (resolvedType == null) {
+ int descriptorIndex = dex.typeIds().get(dexTypeIndex);
+ String descriptor = getDexCacheString(dex, descriptorIndex);
+ resolvedType = InternalNames.getClass(getClassLoader(), descriptor);
+ dexCacheResolvedTypes[dexTypeIndex] = resolvedType;
+ }
+ return resolvedType;
+ }
+
+ /**
+ * Returns a {@code Constructor} object which represents the public
+ * constructor matching the given parameter types.
+ * {@code (Class[]) null} is equivalent to the empty array.
+ *
+ * @throws NoSuchMethodException
+ * if the constructor cannot be found.
+ * @see #getDeclaredConstructor(Class[])
+ */
+ public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException {
+ return getConstructor(parameterTypes, true);
+ }
+
+ /**
+ * Returns a {@code Constructor} object which represents the constructor
+ * matching the specified parameter types that is declared by the class
+ * represented by this {@code Class}.
+ * {@code (Class[]) null} is equivalent to the empty array.
+ *
+ * @throws NoSuchMethodException
+ * if the requested constructor cannot be found.
+ * @see #getConstructor(Class[])
+ */
+ public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
+ throws NoSuchMethodException {
+ return getConstructor(parameterTypes, false);
+ }
+
+ /**
+ * Returns a constructor with the given parameters.
+ *
+ * @param publicOnly true to only return public constructores.
+ * @param parameterTypes argument types to match the constructor's.
+ */
+ private Constructor<T> getConstructor(Class<?>[] parameterTypes, boolean publicOnly)
+ throws NoSuchMethodException {
+ if (parameterTypes == null) {
+ parameterTypes = EmptyArray.CLASS;
+ }
+ for (Class<?> c : parameterTypes) {
+ if (c == null) {
+ throw new NoSuchMethodException("parameter type is null");
+ }
+ }
+ Constructor<T> result = getDeclaredConstructorInternal(parameterTypes);
+ if (result == null || publicOnly && !Modifier.isPublic(result.getAccessFlags())) {
+ throw new NoSuchMethodException("<init> " + Arrays.toString(parameterTypes));
+ }
+ return result;
+ }
+
+ /**
+ * Returns the constructor with the given parameters if it is defined by this class; null
+ * otherwise. This may return a non-public member.
+ *
+ * @param args the types of the parameters to the constructor.
+ */
+ private Constructor<T> getDeclaredConstructorInternal(Class<?>[] args) {
+ if (directMethods != null) {
+ for (ArtMethod m : directMethods) {
+ int modifiers = m.getAccessFlags();
+ if (Modifier.isStatic(modifiers)) {
+ // skip <clinit> which is a static constructor
+ continue;
+ }
+ if (!Modifier.isConstructor(modifiers)) {
+ continue;
+ }
+ if (!ArtMethod.equalConstructorParameters(m, args)) {
+ continue;
+ }
+ return new Constructor<T>(m);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns an array containing {@code Constructor} objects for all public
+ * constructors for this {@code Class}. If there
+ * are no public constructors or if this {@code Class} represents an array
+ * class, a primitive type or void then an empty array is returned.
+ *
+ * @see #getDeclaredConstructors()
+ */
+ public Constructor<?>[] getConstructors() {
+ ArrayList<Constructor<T>> constructors = new ArrayList();
+ getDeclaredConstructors(true, constructors);
+ return constructors.toArray(new Constructor[constructors.size()]);
+ }
+
+ /**
+ * Returns an array containing {@code Constructor} objects for all
+ * constructors declared in the class represented by this {@code Class}. If
+ * there are no constructors or if this {@code Class} represents an array
+ * class, a primitive type or void then an empty array is returned.
+ *
+ * @see #getConstructors()
+ */
+ public Constructor<?>[] getDeclaredConstructors() {
+ ArrayList<Constructor<T>> constructors = new ArrayList();
+ getDeclaredConstructors(false, constructors);
+ return constructors.toArray(new Constructor[constructors.size()]);
+ }
+
+ private void getDeclaredConstructors(boolean publicOnly, List<Constructor<T>> constructors) {
+ if (directMethods != null) {
+ for (ArtMethod m : directMethods) {
+ int modifiers = m.getAccessFlags();
+ if (!publicOnly || Modifier.isPublic(modifiers)) {
+ if (Modifier.isStatic(modifiers)) {
+ // skip <clinit> which is a static constructor
+ continue;
+ }
+ if (Modifier.isConstructor(modifiers)) {
+ constructors.add(new Constructor<T>(m));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a {@code Method} object which represents the method matching the
+ * specified name and parameter types that is declared by the class
+ * represented by this {@code Class}.
+ *
+ * @param name
+ * the requested method's name.
+ * @param parameterTypes
+ * the parameter types of the requested method.
+ * {@code (Class[]) null} is equivalent to the empty array.
+ * @return the method described by {@code name} and {@code parameterTypes}.
+ * @throws NoSuchMethodException
+ * if the requested constructor cannot be found.
+ * @throws NullPointerException
+ * if {@code name} is {@code null}.
+ * @see #getMethod(String, Class[])
+ */
+ public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
+ throws NoSuchMethodException {
+ return getMethod(name, parameterTypes, false);
+ }
+
+ /**
+ * Returns a {@code Method} object which represents the public method with
+ * the specified name and parameter types.
+ * {@code (Class[]) null} is equivalent to the empty array.
+ * This method first searches the
+ * class C represented by this {@code Class}, then the superclasses of C and
+ * finally the interfaces implemented by C and finally the superclasses of C
+ * for a method with matching name.
+ *
+ * @throws NoSuchMethodException
+ * if the method cannot be found.
+ * @see #getDeclaredMethod(String, Class[])
+ */
+ public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException {
+ return getMethod(name, parameterTypes, true);
+ }
+
+ private Method getMethod(String name, Class<?>[] parameterTypes, boolean recursivePublicMethods)
+ throws NoSuchMethodException {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+ if (parameterTypes == null) {
+ parameterTypes = EmptyArray.CLASS;
+ }
+ for (Class<?> c : parameterTypes) {
+ if (c == null) {
+ throw new NoSuchMethodException("parameter type is null");
+ }
+ }
+ Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes)
+ : getDeclaredMethodInternal(name, parameterTypes);
+ // Fail if we didn't find the method or it was expected to be public.
+ if (result == null ||
+ (recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) {
+ throw new NoSuchMethodException(name + " " + Arrays.toString(parameterTypes));
+ }
+ return result;
+ }
+
+ private Method getPublicMethodRecursive(String name, Class<?>[] parameterTypes) {
+ // search superclasses
+ for (Class<?> c = this; c != null; c = c.getSuperclass()) {
+ Method result = c.getDeclaredMethodInternal(name, parameterTypes);
+ if (result != null && Modifier.isPublic(result.getAccessFlags())) {
+ return result;
+ }
+ }
+ // search iftable which has a flattened and uniqued list of interfaces
+ Object[] iftable = ifTable;
+ if (iftable != null) {
+ for (int i = 0; i < iftable.length; i += 2) {
+ Class<?> ifc = (Class<?>) iftable[i];
+ Method result = ifc.getPublicMethodRecursive(name, parameterTypes);
+ if (result != null && Modifier.isPublic(result.getAccessFlags())) {
+ return result;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the method if it is defined by this class; null otherwise. This may return a
+ * non-public member.
+ *
+ * @param name the method name
+ * @param args the method's parameter types
+ */
+ private Method getDeclaredMethodInternal(String name, Class<?>[] args) {
+ // Covariant return types permit the class to define multiple
+ // methods with the same name and parameter types. Prefer to
+ // return a non-synthetic method in such situations. We may
+ // still return a synthetic method to handle situations like
+ // escalated visibility. We never return miranda methods that
+ // were synthesized by the VM.
+ int skipModifiers = Modifier.MIRANDA | Modifier.SYNTHETIC;
+ ArtMethod artMethodResult = null;
+ if (virtualMethods != null) {
+ for (ArtMethod m : virtualMethods) {
+ String methodName = ArtMethod.getMethodName(m);
+ if (!name.equals(methodName)) {
+ continue;
+ }
+ if (!ArtMethod.equalMethodParameters(m, args)) {
+ continue;
+ }
+ int modifiers = m.getAccessFlags();
+ if ((modifiers & skipModifiers) == 0) {
+ return new Method(m);
+ }
+ if ((modifiers & Modifier.MIRANDA) == 0) {
+ // Remember as potential result if it's not a miranda method.
+ artMethodResult = m;
+ }
+ }
+ }
+ if (artMethodResult == null) {
+ if (directMethods != null) {
+ for (ArtMethod m : directMethods) {
+ int modifiers = m.getAccessFlags();
+ if (Modifier.isConstructor(modifiers)) {
+ continue;
+ }
+ String methodName = ArtMethod.getMethodName(m);
+ if (!name.equals(methodName)) {
+ continue;
+ }
+ if (!ArtMethod.equalMethodParameters(m, args)) {
+ continue;
+ }
+ if ((modifiers & skipModifiers) == 0) {
+ return new Method(m);
+ }
+ // Direct methods cannot be miranda methods,
+ // so this potential result must be synthetic.
+ artMethodResult = m;
+ }
+ }
+ }
+ if (artMethodResult == null) {
+ return null;
+ }
+ return new Method(artMethodResult);
+ }
+
+ /**
+ * Returns an array containing {@code Method} objects for all methods
+ * declared in the class represented by this {@code Class}. If there are no
+ * methods or if this {@code Class} represents an array class, a primitive
+ * type or void then an empty array is returned.
+ *
+ * @see #getMethods()
+ */
+ public Method[] getDeclaredMethods() {
+ int initial_size = virtualMethods == null ? 0 : virtualMethods.length;
+ initial_size += directMethods == null ? 0 : directMethods.length;
+ ArrayList<Method> methods = new ArrayList<Method>(initial_size);
+ getDeclaredMethods(false, methods);
+ Method[] result = methods.toArray(new Method[methods.size()]);
+ for (Method m : result) {
+ // Throw NoClassDefFoundError if types cannot be resolved.
+ m.getReturnType();
+ m.getParameterTypes();
+ }
+ return result;
+
+ }
+
+ /**
+ * Returns the list of methods without performing any security checks
+ * first. If no methods exist, an empty array is returned.
+ */
+ private void getDeclaredMethods(boolean publicOnly, List<Method> methods) {
+ if (virtualMethods != null) {
+ for (ArtMethod m : virtualMethods) {
+ int modifiers = m.getAccessFlags();
+ if (!publicOnly || Modifier.isPublic(modifiers)) {
+ // Add non-miranda virtual methods.
+ if ((modifiers & Modifier.MIRANDA) == 0) {
+ methods.add(new Method(m));
+ }
+ }
+ }
+ }
+ if (directMethods != null) {
+ for (ArtMethod m : directMethods) {
+ int modifiers = m.getAccessFlags();
+ if (!publicOnly || Modifier.isPublic(modifiers)) {
+ // Add non-constructor direct/static methods.
+ if (!Modifier.isConstructor(modifiers)) {
+ methods.add(new Method(m));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns an array containing {@code Method} objects for all public methods
+ * for the class C represented by this {@code Class}. Methods may be
+ * declared in C, the interfaces it implements or in the superclasses of C.
+ * The elements in the returned array are in no particular order.
+ *
+ * <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>();
+ getPublicMethodsInternal(methods);
+ /*
+ * Remove duplicate methods defined by superclasses and
+ * interfaces, preferring to keep methods declared by derived
+ * types.
+ */
+ CollectionUtils.removeDuplicates(methods, Method.ORDER_BY_SIGNATURE);
+ return methods.toArray(new Method[methods.size()]);
+ }
+
+ /**
+ * Populates {@code result} with public methods defined by this class, its
+ * superclasses, and all implemented interfaces, including overridden methods.
+ */
+ private void getPublicMethodsInternal(List<Method> result) {
+ getDeclaredMethods(true, result);
+ if (!isInterface()) {
+ // Search superclasses, for interfaces don't search java.lang.Object.
+ for (Class<?> c = superClass; c != null; c = c.superClass) {
+ c.getDeclaredMethods(true, result);
+ }
+ }
+ // Search iftable which has a flattened and uniqued list of interfaces.
+ Object[] iftable = ifTable;
+ if (iftable != null) {
+ for (int i = 0; i < iftable.length; i += 2) {
+ Class<?> ifc = (Class<?>) iftable[i];
+ ifc.getDeclaredMethods(true, result);
+ }
+ }
+ }
+
+ /**
+ * Returns the annotations that are directly defined on the class
+ * represented by this {@code Class}. Annotations that are inherited are not
+ * included in the result. If there are no annotations at all, an empty
+ * array is returned.
+ *
+ * @see #getAnnotations()
+ */
+ @Override public Annotation[] getDeclaredAnnotations() {
+ List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+ return result.toArray(new Annotation[result.size()]);
+ }
+
+ /**
+ * Returns an array containing {@code Class} objects for all classes,
+ * interfaces, enums and annotations that are members of this class.
+ */
+ public Class<?>[] getDeclaredClasses() {
+ return AnnotationAccess.getMemberClasses(this);
+ }
+
+ /**
+ * Returns a {@code Field} object for the field with the given name
+ * which is declared in the class represented by this {@code Class}.
+ *
+ * @throws NoSuchFieldException if the requested field can not be found.
+ * @see #getField(String)
+ */
+ public Field getDeclaredField(String name) throws NoSuchFieldException {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+ Field result = getDeclaredFieldInternal(name);
+ if (result == null) {
+ throw new NoSuchFieldException(name);
+ } else {
+ result.getType(); // Throw NoClassDefFoundError if type cannot be resolved.
+ }
+ return result;
+ }
+
+ /**
+ * Returns an array containing {@code Field} objects for all fields declared
+ * in the class represented by this {@code Class}. If there are no fields or
+ * if this {@code Class} represents an array class, a primitive type or void
+ * then an empty array is returned.
+ *
+ * @see #getFields()
+ */
+ public Field[] getDeclaredFields() {
+ int initial_size = sFields == null ? 0 : sFields.length;
+ initial_size += iFields == null ? 0 : iFields.length;
+ ArrayList<Field> fields = new ArrayList(initial_size);
+ getDeclaredFields(false, fields);
+ Field[] result = fields.toArray(new Field[fields.size()]);
+ for (Field f : result) {
+ f.getType(); // Throw NoClassDefFoundError if type cannot be resolved.
+ }
+ return result;
+ }
+
+ private void getDeclaredFields(boolean publicOnly, List<Field> fields) {
+ if (iFields != null) {
+ for (ArtField f : iFields) {
+ if (!publicOnly || Modifier.isPublic(f.getAccessFlags())) {
+ fields.add(new Field(f));
+ }
+ }
+ }
+ if (sFields != null) {
+ for (ArtField f : sFields) {
+ if (!publicOnly || Modifier.isPublic(f.getAccessFlags())) {
+ fields.add(new Field(f));
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the field if it is defined by this class; null otherwise. This
+ * may return a non-public member.
+ */
+ private Field getDeclaredFieldInternal(String name) {
+ if (iFields != null) {
+ for (ArtField f : iFields) {
+ if (f.getName().equals(name)) {
+ return new Field(f);
+ }
+ }
+ }
+ if (sFields != null) {
+ for (ArtField f : sFields) {
+ if (f.getName().equals(name)) {
+ return new Field(f);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the class that this class is a member of, or {@code null} if this
+ * class is a top-level class, a primitive, an array, or defined within a
+ * method or constructor.
+ */
+ public Class<?> getDeclaringClass() {
+ return AnnotationAccess.getDeclaringClass(this);
+ }
+
+ /**
+ * Returns the class enclosing this class. For most classes this is the same
+ * as the {@link #getDeclaringClass() declaring class}. For classes defined
+ * within a method or constructor (typically anonymous inner classes), this
+ * is the declaring class of that member.
+ */
+ public Class<?> getEnclosingClass() {
+ Class<?> declaringClass = getDeclaringClass();
+ if (declaringClass != null) {
+ return declaringClass;
+ }
+ AccessibleObject member = AnnotationAccess.getEnclosingMethodOrConstructor(this);
+ return member != null
+ ? ((Member) member).getDeclaringClass()
+ : null;
+ }
+
+ /**
+ * Returns the enclosing {@code Constructor} of this {@code Class}, if it is an
+ * anonymous or local/automatic class; otherwise {@code null}.
+ */
+ public Constructor<?> getEnclosingConstructor() {
+ if (classNameImpliesTopLevel()) {
+ return null;
+ }
+ AccessibleObject result = AnnotationAccess.getEnclosingMethodOrConstructor(this);
+ return result instanceof Constructor ? (Constructor<?>) result : null;
+ }
+
+ /**
+ * Returns the enclosing {@code Method} of this {@code Class}, if it is an
+ * anonymous or local/automatic class; otherwise {@code null}.
+ */
+ public Method getEnclosingMethod() {
+ if (classNameImpliesTopLevel()) {
+ return null;
+ }
+ AccessibleObject result = AnnotationAccess.getEnclosingMethodOrConstructor(this);
+ return result instanceof Method ? (Method) result : null;
+ }
+
+ /**
+ * Returns true if this class is definitely a top level class, or false if
+ * a more expensive check like {@link #getEnclosingClass()} is necessary.
+ *
+ * <p>This is a hack that exploits an implementation detail of all Java
+ * language compilers: generated names always contain "$". As it is possible
+ * for a top level class to be named with a "$", a false result <strong>does
+ * not</strong> indicate that this isn't a top-level class.
+ */
+ private boolean classNameImpliesTopLevel() {
+ return !getName().contains("$");
+ }
+
+ /**
+ * Returns the {@code enum} constants associated with this {@code Class}.
+ * Returns {@code null} if this {@code Class} does not represent an {@code
+ * enum} type.
+ */
+ @SuppressWarnings("unchecked") // we only cast after confirming that this class is an enum
+ public T[] getEnumConstants() {
+ if (!isEnum()) {
+ return null;
+ }
+ return (T[]) Enum.getSharedConstants((Class) this).clone();
+ }
+
+ /**
+ * Returns a {@code Field} object which represents the public field with the
+ * given name. This method first searches the class C represented by
+ * this {@code Class}, then the interfaces implemented by C and finally the
+ * superclasses of C.
+ *
+ * @throws NoSuchFieldException
+ * if the field cannot be found.
+ * @see #getDeclaredField(String)
+ */
+ public Field getField(String name) throws NoSuchFieldException {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+ Field result = getPublicFieldRecursive(name);
+ if (result == null) {
+ throw new NoSuchFieldException(name);
+ } else {
+ result.getType(); // Throw NoClassDefFoundError if type cannot be resolved.
+ }
+ return result;
+ }
+
+ private Field getPublicFieldRecursive(String name) {
+ // search superclasses
+ for (Class<?> c = this; c != null; c = c.superClass) {
+ Field result = c.getDeclaredFieldInternal(name);
+ if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
+ return result;
+ }
+ }
+
+ // search iftable which has a flattened and uniqued list of interfaces
+ if (ifTable != null) {
+ for (int i = 0; i < ifTable.length; i += 2) {
+ Class<?> ifc = (Class<?>) ifTable[i];
+ Field result = ifc.getPublicFieldRecursive(name);
+ if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
+ return result;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns an array containing {@code Field} objects for all public fields
+ * for the class C represented by this {@code Class}. Fields may be declared
+ * in C, the interfaces it implements or in the superclasses of C. The
+ * elements in the returned array are in no particular order.
+ *
+ * <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);
+ Field[] result = fields.toArray(new Field[fields.size()]);
+ for (Field f : result) {
+ f.getType(); // Throw NoClassDefFoundError if type cannot be resolved.
+ }
+ return result;
+ }
+
+ /**
+ * Populates {@code result} with public fields defined by this class, its
+ * superclasses, and all implemented interfaces.
+ */
+ private void getPublicFieldsRecursive(List<Field> result) {
+ // search superclasses
+ for (Class<?> c = this; c != null; c = c.superClass) {
+ c.getDeclaredFields(true, result);
+ }
+
+ // search iftable which has a flattened and uniqued list of interfaces
+ Object[] iftable = ifTable;
+ if (iftable != null) {
+ for (int i = 0; i < iftable.length; i += 2) {
+ Class<?> ifc = (Class<?>) iftable[i];
+ ifc.getDeclaredFields(true, result);
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link Type}s of the interfaces that this {@code Class} directly
+ * implements. If the {@code Class} represents a primitive type or {@code
+ * void} then an empty array is returned.
+ */
+ public Type[] getGenericInterfaces() {
+ 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();
+ 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.
+ *
+ * <p>This method only returns directly-implemented interfaces, and does not
+ * include interfaces implemented by superclasses or superinterfaces of any
+ * implemented interfaces.
+ */
+ public Class<?>[] getInterfaces() {
+ if (isArray()) {
+ return new Class<?>[] { Cloneable.class, Serializable.class };
+ } else if (isProxy()) {
+ return getProxyInterfaces();
+ }
+ Dex dex = getDex();
+ if (dex == null) {
+ return EmptyArray.CLASS;
+ }
+ short[] interfaces = dex.interfaceTypeIndicesFromClassDefIndex(dexClassDefIndex);
+ Class<?>[] result = new Class<?>[interfaces.length];
+ for (int i = 0; i < interfaces.length; i++) {
+ result[i] = getDexCacheType(dex, interfaces[i]);
+ }
+ return result;
+ }
+
+ // Returns the interfaces that this proxy class directly implements.
+ private native Class<?>[] getProxyInterfaces();
+
+ /**
+ * Returns an integer that represents the modifiers of the class represented
+ * by this {@code Class}. The returned value is a combination of bits
+ * defined by constants in the {@link Modifier} class.
+ */
+ public int getModifiers() {
+ // Array classes inherit modifiers from their component types, but in the case of arrays
+ // of an inner class, the class file may contain "fake" access flags because it's not valid
+ // for a top-level class to private, say. The real access flags are stored in the InnerClass
+ // attribute, so we need to make sure we drill down to the inner class: the accessFlags
+ // field is not the value we want to return, and the synthesized array class does not itself
+ // have an InnerClass attribute. https://code.google.com/p/android/issues/detail?id=56267
+ if (isArray()) {
+ int componentModifiers = getComponentType().getModifiers();
+ if ((componentModifiers & Modifier.INTERFACE) != 0) {
+ componentModifiers &= ~(Modifier.INTERFACE | Modifier.STATIC);
+ }
+ return Modifier.ABSTRACT | Modifier.FINAL | componentModifiers;
+ }
+ int JAVA_FLAGS_MASK = 0xffff;
+ int modifiers = AnnotationAccess.getInnerClassFlags(this, accessFlags & JAVA_FLAGS_MASK);
+ return modifiers & JAVA_FLAGS_MASK;
+ }
+
+ /**
+ * Returns the name of the class represented by this {@code Class}. For a
+ * description of the format which is used, see the class definition of
+ * {@link Class}.
+ */
+ public String getName() {
+ String result = name;
+ return (result == null) ? (name = getNameNative()) : result;
+ }
+
+ private native String getNameNative();
+
+ /**
+ * Returns the simple name of the class represented by this {@code Class} as
+ * defined in the source code. If there is no name (that is, the class is
+ * anonymous) then an empty string is returned. If the receiver is an array
+ * then the name of the underlying type with square braces appended (for
+ * example {@code "Integer[]"}) is returned.
+ *
+ * @return the simple name of the class represented by this {@code Class}.
+ */
+ public String getSimpleName() {
+ if (isArray()) {
+ return getComponentType().getSimpleName() + "[]";
+ }
+
+ if (isAnonymousClass()) {
+ return "";
+ }
+
+ if (isMemberClass() || isLocalClass()) {
+ return getInnerClassName();
+ }
+
+ String name = getName();
+ int dot = name.lastIndexOf('.');
+ if (dot != -1) {
+ return name.substring(dot + 1);
+ }
+
+ return name;
+ }
+
+ /**
+ * Returns the simple name of a member or local class, or null otherwise.
+ */
+ private String getInnerClassName() {
+ return AnnotationAccess.getInnerClassName(this);
+ }
+
+ /**
+ * Returns null.
+ */
+ public ProtectionDomain getProtectionDomain() {
+ return null;
+ }
+
+ /**
+ * Returns the URL of the given resource, or null if the resource is not found.
+ * The mapping between the resource name and the URL is managed by the class' class loader.
+ *
+ * @see ClassLoader
+ */
+ public URL getResource(String resourceName) {
+ // Get absolute resource name, but without the leading slash
+ if (resourceName.startsWith("/")) {
+ resourceName = resourceName.substring(1);
+ } else {
+ String pkg = getName();
+ int dot = pkg.lastIndexOf('.');
+ if (dot != -1) {
+ pkg = pkg.substring(0, dot).replace('.', '/');
+ } else {
+ pkg = "";
+ }
+
+ resourceName = pkg + "/" + resourceName;
+ }
+
+ // Delegate to proper class loader
+ ClassLoader loader = getClassLoader();
+ if (loader != null) {
+ return loader.getResource(resourceName);
+ } else {
+ return ClassLoader.getSystemResource(resourceName);
+ }
+ }
+
+ /**
+ * Returns a read-only stream for the contents of the given resource, or null if the resource
+ * is not found.
+ * The mapping between the resource name and the stream is managed by the class' class loader.
+ *
+ * @see ClassLoader
+ */
+ public InputStream getResourceAsStream(String resourceName) {
+ // Get absolute resource name, but without the leading slash
+ if (resourceName.startsWith("/")) {
+ resourceName = resourceName.substring(1);
+ } else {
+ String pkg = getName();
+ int dot = pkg.lastIndexOf('.');
+ if (dot != -1) {
+ pkg = pkg.substring(0, dot).replace('.', '/');
+ } else {
+ pkg = "";
+ }
+
+ resourceName = pkg + "/" + resourceName;
+ }
+
+ // Delegate to proper class loader
+ ClassLoader loader = getClassLoader();
+ if (loader != null) {
+ return loader.getResourceAsStream(resourceName);
+ } else {
+ return ClassLoader.getSystemResourceAsStream(resourceName);
+ }
+ }
+
+ /**
+ * Returns null. (On Android, a {@code ClassLoader} can load classes from multiple dex files.
+ * All classes from any given dex file will have the same signers, but different dex
+ * files may have different signers. This does not fit well with the original
+ * {@code ClassLoader}-based model of {@code getSigners}.)
+ */
+ public Object[] getSigners() {
+ // See http://code.google.com/p/android/issues/detail?id=1766.
+ return null;
+ }
+
+ /**
+ * Returns the {@code Class} object which represents the superclass of the
+ * class represented by this {@code Class}. If this {@code Class} represents
+ * the {@code Object} class, a primitive type, an interface or void then the
+ * method returns {@code null}. If this {@code Class} represents an array
+ * class then the {@code Object} class is returned.
+ */
+ public Class<? super T> getSuperclass() {
+ // For interfaces superClass is Object (which agrees with the JNI spec)
+ // but not with the expected behavior here.
+ if (isInterface()) {
+ return null;
+ } else {
+ return superClass;
+ }
+ }
+
+ /**
+ * Returns an array containing {@code TypeVariable} objects for type
+ * variables declared by the generic class represented by this {@code
+ * Class}. Returns an empty array if the class is not generic.
+ */
+ @SuppressWarnings("unchecked")
+ @Override public synchronized TypeVariable<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.Modifier
+ return (accessFlags & ACC_ANNOTATION) != 0;
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ return AnnotationAccess.isAnnotationPresent(this, annotationType);
+ }
+
+ /**
+ * Tests whether the class represented by this {@code Class} is
+ * anonymous.
+ */
+ public boolean isAnonymousClass() {
+ return AnnotationAccess.isAnonymousClass(this);
+ }
+
+ /**
+ * Tests whether the class represented by this {@code Class} is an array class.
+ */
+ public boolean isArray() {
+ return getComponentType() != null;
+ }
+
+ /**
+ * Is this a runtime created proxy class?
+ *
+ * @hide
+ */
+ public boolean isProxy() {
+ return (accessFlags & 0x00040000) != 0;
+ }
+
+ /**
+ * Can {@code c} be assigned to this class? For example, String can be assigned to Object
+ * (by an upcast), however, an Object cannot be assigned to a String as a potentially exception
+ * throwing downcast would be necessary. Similarly for interfaces, a class that implements (or
+ * an interface that extends) another can be assigned to its parent, but not vice-versa. All
+ * Classes may assign to themselves. Classes for primitive types may not assign to each other.
+ *
+ * @param c the class to check.
+ * @return {@code true} if {@code c} can be assigned to the class
+ * represented by this {@code Class}; {@code false} otherwise.
+ * @throws NullPointerException if {@code c} is {@code null}.
+ */
+ public boolean isAssignableFrom(Class<?> c) {
+ if (this == c) {
+ return true; // Can always assign to things of the same type.
+ } else if (this == Object.class) {
+ return !c.isPrimitive(); // Can assign any reference to java.lang.Object.
+ } else if (isArray()) {
+ return c.isArray() && componentType.isAssignableFrom(c.componentType);
+ } else if (isInterface()) {
+ // Search iftable which has a flattened and uniqued list of interfaces.
+ Object[] iftable = c.ifTable;
+ if (iftable != null) {
+ for (int i = 0; i < iftable.length; i += 2) {
+ Class<?> ifc = (Class<?>) iftable[i];
+ if (ifc == this) {
+ return true;
+ }
+ }
+ }
+ return false;
+ } else {
+ if (!c.isInterface()) {
+ for (c = c.superClass; c != null; c = c.superClass) {
+ if (c == this) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Tests whether the class represented by this {@code Class} is an
+ * {@code enum}.
+ */
+ public boolean isEnum() {
+ return (getSuperclass() == Enum.class) && ((accessFlags & 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 boolean isInstance(Object object) {
+ if (object == null) {
+ return false;
+ }
+ return isAssignableFrom(object.getClass());
+ }
+
+ /**
+ * Tests whether this {@code Class} represents an interface.
+ */
+ public boolean isInterface() {
+ return (accessFlags & Modifier.INTERFACE) != 0;
+ }
+
+ /**
+ * Tests whether the class represented by this {@code Class} is defined
+ * locally.
+ */
+ public boolean isLocalClass() {
+ return !classNameImpliesTopLevel()
+ && AnnotationAccess.getEnclosingMethodOrConstructor(this) != null
+ && !isAnonymousClass();
+ }
+
+ /**
+ * Tests whether the class represented by this {@code Class} is a member
+ * class.
+ */
+ public boolean isMemberClass() {
+ return getDeclaringClass() != null;
+ }
+
+ /**
+ * Tests whether this {@code Class} represents a primitive type.
+ */
+ public boolean isPrimitive() {
+ return primitiveType != 0;
+ }
+
+ /**
+ * Tests whether this {@code Class} represents a synthetic type.
+ */
+ public boolean isSynthetic() {
+ final int ACC_SYNTHETIC = 0x1000; // not public in reflect.Modifier
+ return (accessFlags & ACC_SYNTHETIC) != 0;
+ }
+
+ /**
+ * Indicates whether this {@code Class} or its parents override finalize.
+ *
+ * @hide
+ */
+ public boolean isFinalizable() {
+ final int ACC_CLASS_IS_FINALIZABLE = 0x80000000; // not public in reflect.Modifier
+ return (accessFlags & ACC_CLASS_IS_FINALIZABLE) != 0;
+ }
+
+ /**
+ * Returns a new instance of the class represented by this {@code Class},
+ * created by invoking the default (that is, zero-argument) constructor. If
+ * there is no such constructor, or if the creation fails (either because of
+ * a lack of available memory or because an exception is thrown by the
+ * constructor), an {@code InstantiationException} is thrown. If the default
+ * constructor exists but is not accessible from the context where this
+ * method is invoked, an {@code IllegalAccessException} is thrown.
+ *
+ * @throws IllegalAccessException
+ * if the default constructor is not visible.
+ * @throws InstantiationException
+ * if the instance cannot be created.
+ */
+ public T newInstance() throws InstantiationException, IllegalAccessException {
+ if (isPrimitive() || isInterface() || isArray() || Modifier.isAbstract(accessFlags)) {
+ throw new InstantiationException(this + " cannot be instantiated");
+ }
+ Class<?> caller = VMStack.getStackClass1();
+ if (!caller.canAccess(this)) {
+ throw new IllegalAccessException(this + " is not accessible from " + caller);
+ }
+ Constructor<T> init;
+ try {
+ init = getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ InstantiationException t =
+ new InstantiationException(this + " has no zero argument constructor");
+ t.initCause(e);
+ throw t;
+ }
+ if (!caller.canAccessMember(this, init.getAccessFlags())) {
+ throw new IllegalAccessException(init + " is not accessible from " + caller);
+ }
+ try {
+ return init.newInstance();
+ } catch (InvocationTargetException e) {
+ InstantiationException t = new InstantiationException(this);
+ t.initCause(e);
+ throw t;
+ }
+ }
+
+ private boolean canAccess(Class<?> c) {
+ if(Modifier.isPublic(c.accessFlags)) {
+ return true;
+ }
+ return inSamePackage(c);
+ }
+
+ private boolean canAccessMember(Class<?> memberClass, int memberModifiers) {
+ if (memberClass == this || Modifier.isPublic(memberModifiers)) {
+ return true;
+ }
+ if (Modifier.isPrivate(memberModifiers)) {
+ return false;
+ }
+ if (Modifier.isProtected(memberModifiers)) {
+ for (Class<?> parent = this.superClass; parent != null; parent = parent.superClass) {
+ if (parent == memberClass) {
+ return true;
+ }
+ }
+ }
+ return inSamePackage(memberClass);
+ }
+
+ private boolean inSamePackage(Class<?> c) {
+ if (classLoader != c.classLoader) {
+ return false;
+ }
+ String packageName1 = getPackageName$();
+ String packageName2 = c.getPackageName$();
+ if (packageName1 == null) {
+ return packageName2 == null;
+ } else if (packageName2 == null) {
+ return false;
+ } else {
+ return packageName1.equals(packageName2);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (isPrimitive()) {
+ return getSimpleName();
+ } else {
+ return (isInterface() ? "interface " : "class ") + getName();
+ }
+ }
+
+ /**
+ * Returns the {@code Package} of which the class represented by this
+ * {@code Class} is a member. Returns {@code null} if no {@code Package}
+ * object was created by the class loader of the class.
+ */
+ public Package getPackage() {
+ // TODO This might be a hack, but the VM doesn't have the necessary info.
+ ClassLoader loader = getClassLoader();
+ if (loader != null) {
+ String packageName = getPackageName$();
+ return packageName != null ? loader.getPackage(packageName) : null;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the package name of this class. This returns null for classes in
+ * the default package.
+ *
+ * @hide
+ */
+ public String getPackageName$() {
+ String name = getName();
+ int last = name.lastIndexOf('.');
+ return last == -1 ? null : name.substring(0, last);
+ }
+
+ /**
+ * Returns the assertion status for the class represented by this {@code
+ * Class}. Assertion is enabled / disabled based on the class loader,
+ * package or class default at runtime.
+ */
+ public boolean desiredAssertionStatus() {
+ return false;
+ }
+
+ /**
+ * Casts this {@code Class} to represent a subclass of the given class.
+ * If successful, this {@code Class} is returned; otherwise a {@code
+ * ClassCastException} is thrown.
+ *
+ * @throws ClassCastException
+ * if this {@code Class} cannot be cast to the given type.
+ */
+ @SuppressWarnings("unchecked")
+ public <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);
+ }
+
+ /**
+ * The class def of this class in its own Dex, or -1 if there is no class def.
+ *
+ * @hide
+ */
+ public int getDexClassDefIndex() {
+ return (dexClassDefIndex == 65535) ? -1 : 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() {
+ int typeIndex = dexTypeIndex;
+ if (typeIndex != 65535) {
+ return typeIndex;
+ }
+ synchronized (this) {
+ typeIndex = dexTypeIndex;
+ if (typeIndex == 65535) {
+ if (dexClassDefIndex >= 0) {
+ typeIndex = getDex().typeIndexFromClassDefIndex(dexClassDefIndex);
+ } else {
+ typeIndex = getDex().findTypeIndex(InternalNames.getInternalName(this));
+ if (typeIndex < 0) {
+ typeIndex = -1;
+ }
+ }
+ dexTypeIndex = typeIndex;
+ }
+ }
+ return typeIndex;
+ }
+
+ /**
+ * 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);
+ }
+
+ 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/libart/src/main/java/java/lang/ClassLoader.java b/libart/src/main/java/java/lang/ClassLoader.java
new file mode 100644
index 0000000..fb2eb8f
--- /dev/null
+++ b/libart/src/main/java/java/lang/ClassLoader.java
@@ -0,0 +1,856 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+import dalvik.system.PathClassLoader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.security.ProtectionDomain;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Loads classes and resources from a repository. One or more class loaders are
+ * installed at runtime. These are consulted whenever the runtime system needs a
+ * specific class that is not yet available in-memory. Typically, class loaders
+ * are grouped into a tree where child class loaders delegate all requests to
+ * parent class loaders. Only if the parent class loader cannot satisfy the
+ * request, the child class loader itself tries to handle it.
+ * <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>();
+
+ /**
+ * To avoid unloading individual classes, {@link java.lang.reflect.Proxy}
+ * only generates one class for each set of interfaces. This maps sets of
+ * interfaces to the proxy class that implements all of them. It is declared
+ * here so that these generated classes can be unloaded with their class
+ * loader.
+ *
+ * @hide
+ */
+ public final Map<Set<Class<?>>, Class<?>> proxyCache
+ = Collections.synchronizedMap(new HashMap<Set<Class<?>>, Class<?>>());
+
+ /**
+ * Create the system class loader. Note this is NOT the bootstrap class
+ * loader (which is managed by the VM). We use a null value for the parent
+ * to indicate that the bootstrap loader is our parent.
+ */
+ private static ClassLoader createSystemClassLoader() {
+ String classPath = System.getProperty("java.class.path", ".");
+
+ // String[] paths = classPath.split(":");
+ // URL[] urls = new URL[paths.length];
+ // for (int i = 0; i < paths.length; i++) {
+ // try {
+ // urls[i] = new URL("file://" + paths[i]);
+ // }
+ // catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+ // }
+ //
+ // return new java.net.URLClassLoader(urls, null);
+
+ // TODO Make this a java.net.URLClassLoader once we have those?
+ return new PathClassLoader(classPath, BootClassLoader.getInstance());
+ }
+
+ /**
+ * Returns the system class loader. This is the parent for new
+ * {@code ClassLoader} instances and is typically the class loader used to
+ * start the application.
+ */
+ public static ClassLoader getSystemClassLoader() {
+ return SystemClassLoader.loader;
+ }
+
+ /**
+ * Finds the URL of the resource with the specified name. The system class
+ * loader's resource lookup algorithm is used to find the resource.
+ *
+ * @return the {@code URL} object for the requested resource or {@code null}
+ * if the resource can not be found.
+ * @param resName
+ * the name of the resource to find.
+ * @see Class#getResource
+ */
+ public static URL getSystemResource(String resName) {
+ return SystemClassLoader.loader.getResource(resName);
+ }
+
+ /**
+ * Returns an enumeration of URLs for the resource with the specified name.
+ * The system class loader's resource lookup algorithm is used to find the
+ * resource.
+ *
+ * @return an enumeration of {@code URL} objects containing the requested
+ * resources.
+ * @param resName
+ * the name of the resource to find.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ public static Enumeration<URL> getSystemResources(String resName) throws IOException {
+ return SystemClassLoader.loader.getResources(resName);
+ }
+
+ /**
+ * Returns a stream for the resource with the specified name. The system
+ * class loader's resource lookup algorithm is used to find the resource.
+ * Basically, the contents of the java.class.path are searched in order,
+ * looking for a path which matches the specified resource.
+ *
+ * @return a stream for the resource or {@code null}.
+ * @param resName
+ * the name of the resource to find.
+ * @see Class#getResourceAsStream
+ */
+ public static InputStream getSystemResourceAsStream(String resName) {
+ return SystemClassLoader.loader.getResourceAsStream(resName);
+ }
+
+ /**
+ * Constructs a new instance of this class with the system class loader as
+ * its parent.
+ */
+ protected ClassLoader() {
+ this(getSystemClassLoader(), false);
+ }
+
+ /**
+ * Constructs a new instance of this class with the specified class loader
+ * as its parent.
+ *
+ * @param parentLoader
+ * The {@code ClassLoader} to use as the new class loader's
+ * parent.
+ */
+ protected ClassLoader(ClassLoader parentLoader) {
+ this(parentLoader, false);
+ }
+
+ /*
+ * constructor for the BootClassLoader which needs parent to be null.
+ */
+ ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
+ if (parentLoader == null && !nullAllowed) {
+ throw new NullPointerException("parentLoader == null && !nullAllowed");
+ }
+ parent = parentLoader;
+ }
+
+ /**
+ * Constructs a new class from an array of bytes containing a class
+ * definition in class file format.
+ *
+ * @param classRep
+ * the memory image of a class file.
+ * @param offset
+ * the offset into {@code classRep}.
+ * @param length
+ * the length of the class file.
+ * @return the {@code Class} object created from the specified subset of
+ * data in {@code classRep}.
+ * @throws ClassFormatError
+ * if {@code classRep} does not contain a valid class.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0}, {@code length < 0} or if
+ * {@code offset + length} is greater than the length of
+ * {@code classRep}.
+ * @deprecated Use {@link #defineClass(String, byte[], int, int)}
+ */
+ @Deprecated
+ protected final Class<?> defineClass(byte[] classRep, int offset, int length)
+ throws ClassFormatError {
+ throw new UnsupportedOperationException("can't load this type of class file");
+ }
+
+ /**
+ * Constructs a new class from an array of bytes containing a class
+ * definition in class file format.
+ *
+ * @param className
+ * the expected name of the new class, may be {@code null} if not
+ * known.
+ * @param classRep
+ * the memory image of a class file.
+ * @param offset
+ * the offset into {@code classRep}.
+ * @param length
+ * the length of the class file.
+ * @return the {@code Class} object created from the specified subset of
+ * data in {@code classRep}.
+ * @throws ClassFormatError
+ * if {@code classRep} does not contain a valid class.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0}, {@code length < 0} or if
+ * {@code offset + length} is greater than the length of
+ * {@code classRep}.
+ */
+ protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length)
+ throws ClassFormatError {
+ throw new UnsupportedOperationException("can't load this type of class file");
+ }
+
+ /**
+ * Constructs a new class from an array of bytes containing a class
+ * definition in class file format and assigns the specified protection
+ * domain to the new class. If the provided protection domain is
+ * {@code null} then a default protection domain is assigned to the class.
+ *
+ * @param className
+ * the expected name of the new class, may be {@code null} if not
+ * known.
+ * @param classRep
+ * the memory image of a class file.
+ * @param offset
+ * the offset into {@code classRep}.
+ * @param length
+ * the length of the class file.
+ * @param protectionDomain
+ * the protection domain to assign to the loaded class, may be
+ * {@code null}.
+ * @return the {@code Class} object created from the specified subset of
+ * data in {@code classRep}.
+ * @throws ClassFormatError
+ * if {@code classRep} does not contain a valid class.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0}, {@code length < 0} or if
+ * {@code offset + length} is greater than the length of
+ * {@code classRep}.
+ * @throws NoClassDefFoundError
+ * if {@code className} is not equal to the name of the class
+ * contained in {@code classRep}.
+ */
+ protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length,
+ ProtectionDomain protectionDomain) throws java.lang.ClassFormatError {
+ throw new UnsupportedOperationException("can't load this type of class file");
+ }
+
+ /**
+ * Defines a new class with the specified name, byte code from the byte
+ * buffer and the optional protection domain. If the provided protection
+ * domain is {@code null} then a default protection domain is assigned to
+ * the class.
+ *
+ * @param name
+ * the expected name of the new class, may be {@code null} if not
+ * known.
+ * @param b
+ * the byte buffer containing the byte code of the new class.
+ * @param protectionDomain
+ * the protection domain to assign to the loaded class, may be
+ * {@code null}.
+ * @return the {@code Class} object created from the data in {@code b}.
+ * @throws ClassFormatError
+ * if {@code b} does not contain a valid class.
+ * @throws NoClassDefFoundError
+ * if {@code className} is not equal to the name of the class
+ * contained in {@code b}.
+ */
+ protected final Class<?> defineClass(String name, ByteBuffer b,
+ ProtectionDomain protectionDomain) throws ClassFormatError {
+
+ byte[] temp = new byte[b.remaining()];
+ b.get(temp);
+ return defineClass(name, temp, 0, temp.length, protectionDomain);
+ }
+
+ /**
+ * Overridden by subclasses, throws a {@code ClassNotFoundException} by
+ * default. This method is called by {@code loadClass} after the parent
+ * {@code ClassLoader} has failed to find a loaded class of the same name.
+ *
+ * @param className
+ * the name of the class to look for.
+ * @return the {@code Class} object that is found.
+ * @throws ClassNotFoundException
+ * if the class cannot be found.
+ */
+ protected Class<?> findClass(String className) throws ClassNotFoundException {
+ throw new ClassNotFoundException(className);
+ }
+
+ /**
+ * Returns the class with the specified name if it has already been loaded
+ * by the VM or {@code null} if it has not yet been loaded.
+ *
+ * @param className
+ * the name of the class to look for.
+ * @return the {@code Class} object or {@code null} if the requested class
+ * has not been loaded.
+ */
+ protected final Class<?> findLoadedClass(String className) {
+ ClassLoader loader;
+ if (this == BootClassLoader.getInstance())
+ loader = null;
+ else
+ loader = this;
+ return VMClassLoader.findLoadedClass(loader, className);
+ }
+
+ /**
+ * Finds the class with the specified name, loading it using the system
+ * class loader if necessary.
+ *
+ * @param className
+ * the name of the class to look for.
+ * @return the {@code Class} object with the requested {@code className}.
+ * @throws ClassNotFoundException
+ * if the class can not be found.
+ */
+ protected final Class<?> findSystemClass(String className) throws ClassNotFoundException {
+ return Class.forName(className, false, getSystemClassLoader());
+ }
+
+ /**
+ * Returns this class loader's parent.
+ *
+ * @return this class loader's parent or {@code null}.
+ */
+ public final ClassLoader getParent() {
+ return parent;
+ }
+
+ /**
+ * Returns the URL of the resource with the specified name. This
+ * implementation first tries to use the parent class loader to find the
+ * resource; if this fails then {@link #findResource(String)} is called to
+ * find the requested resource.
+ *
+ * @param resName
+ * the name of the resource to find.
+ * @return the {@code URL} object for the requested resource or {@code null}
+ * if the resource can not be found
+ * @see Class#getResource
+ */
+ public URL getResource(String resName) {
+ URL resource = parent.getResource(resName);
+ if (resource == null) {
+ resource = findResource(resName);
+ }
+ return resource;
+ }
+
+ /**
+ * Returns an enumeration of URLs for the resource with the specified name.
+ * This implementation first uses this class loader's parent to find the
+ * resource, then it calls {@link #findResources(String)} to get additional
+ * URLs. The returned enumeration contains the {@code URL} objects of both
+ * find operations.
+ *
+ * @return an enumeration of {@code URL} objects for the requested resource.
+ * @param resName
+ * the name of the resource to find.
+ * @throws IOException
+ * if an I/O error occurs.
+ */
+ @SuppressWarnings("unchecked")
+ public Enumeration<URL> getResources(String resName) throws IOException {
+
+ Enumeration<URL> first = parent.getResources(resName);
+ Enumeration<URL> 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) {
+ ClassNotFoundException suppressed = null;
+ try {
+ clazz = parent.loadClass(className, false);
+ } catch (ClassNotFoundException e) {
+ suppressed = e;
+ }
+
+ if (clazz == null) {
+ try {
+ clazz = findClass(className);
+ } catch (ClassNotFoundException e) {
+ e.addSuppressed(suppressed);
+ throw e;
+ }
+ }
+ }
+
+ return clazz;
+ }
+
+ /**
+ * Forces a class to be linked (initialized). If the class has already been
+ * linked this operation has no effect.
+ * <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 final Enumeration<URL> first;
+
+ private final Enumeration<URL> second;
+
+ public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ @Override
+ public boolean hasMoreElements() {
+ return first.hasMoreElements() || second.hasMoreElements();
+ }
+
+ @Override
+ public URL nextElement() {
+ if (first.hasMoreElements()) {
+ return first.nextElement();
+ } else {
+ return second.nextElement();
+ }
+ }
+
+}
+
+/**
+ * Provides an explicit representation of the boot class loader. It sits at the
+ * head of the class loader chain and delegates requests to the VM's internal
+ * class loading mechanism.
+ */
+class BootClassLoader extends ClassLoader {
+
+ private static BootClassLoader instance;
+
+ @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
+ public static synchronized BootClassLoader getInstance() {
+ if (instance == null) {
+ instance = new BootClassLoader();
+ }
+
+ return instance;
+ }
+
+ public BootClassLoader() {
+ super(null, true);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ return Class.classForName(name, false, null);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return VMClassLoader.getResource(name);
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected Enumeration<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/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java
new file mode 100644
index 0000000..1422c13
--- /dev/null
+++ b/libart/src/main/java/java/lang/Daemons.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+import dalvik.system.VMRuntime;
+import java.lang.ref.FinalizerReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.TimeoutException;
+import libcore.util.EmptyArray;
+
+/**
+ * Calls Object.finalize() on objects in the finalizer reference queue. The VM
+ * will abort if any finalize() call takes more than the maximum finalize time
+ * to complete.
+ *
+ * @hide
+ */
+public final class Daemons {
+ private static final int NANOS_PER_MILLI = 1000 * 1000;
+ private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
+ private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
+
+ public static void start() {
+ ReferenceQueueDaemon.INSTANCE.start();
+ FinalizerDaemon.INSTANCE.start();
+ FinalizerWatchdogDaemon.INSTANCE.start();
+ HeapTrimmerDaemon.INSTANCE.start();
+ GCDaemon.INSTANCE.start();
+ }
+
+ public static void stop() {
+ ReferenceQueueDaemon.INSTANCE.stop();
+ FinalizerDaemon.INSTANCE.stop();
+ FinalizerWatchdogDaemon.INSTANCE.stop();
+ HeapTrimmerDaemon.INSTANCE.stop();
+ GCDaemon.INSTANCE.stop();
+ }
+
+ /**
+ * A background task that provides runtime support to the application.
+ * Daemons can be stopped and started, but only so that the zygote can be a
+ * single-threaded process when it forks.
+ */
+ private static abstract class Daemon implements Runnable {
+ private Thread thread;
+
+ public synchronized void start() {
+ if (thread != null) {
+ throw new IllegalStateException("already running");
+ }
+ thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName());
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ public abstract void run();
+
+ /**
+ * Returns true while the current thread should continue to run; false
+ * when it should return.
+ */
+ protected synchronized boolean isRunning() {
+ return thread != null;
+ }
+
+ public synchronized void interrupt() {
+ if (thread == null) {
+ throw new IllegalStateException("not running");
+ }
+ thread.interrupt();
+ }
+
+ /**
+ * Waits for the runtime thread to stop. This interrupts the thread
+ * currently running the runnable and then waits for it to exit.
+ */
+ public void stop() {
+ Thread threadToStop;
+ synchronized (this) {
+ threadToStop = thread;
+ thread = null;
+ }
+ if (threadToStop == null) {
+ throw new IllegalStateException("not running");
+ }
+ threadToStop.interrupt();
+ while (true) {
+ try {
+ threadToStop.join();
+ return;
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Returns the current stack trace of the thread, or an empty stack trace
+ * if the thread is not currently running.
+ */
+ public synchronized StackTraceElement[] getStackTrace() {
+ return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
+ }
+ }
+
+ /**
+ * This heap management thread moves elements from the garbage collector's
+ * pending list to the managed reference queue.
+ */
+ private static class ReferenceQueueDaemon extends Daemon {
+ private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
+
+ @Override public void run() {
+ while (isRunning()) {
+ Reference<?> list;
+ try {
+ synchronized (ReferenceQueue.class) {
+ while (ReferenceQueue.unenqueued == null) {
+ ReferenceQueue.class.wait();
+ }
+ list = ReferenceQueue.unenqueued;
+ ReferenceQueue.unenqueued = null;
+ }
+ } catch (InterruptedException e) {
+ continue;
+ }
+ enqueue(list);
+ }
+ }
+
+ private void enqueue(Reference<?> list) {
+ while (list != null) {
+ Reference<?> reference;
+ // pendingNext is owned by the GC so no synchronization is required
+ if (list == list.pendingNext) {
+ reference = list;
+ reference.pendingNext = null;
+ list = null;
+ } else {
+ reference = list.pendingNext;
+ list.pendingNext = reference.pendingNext;
+ reference.pendingNext = null;
+ }
+ reference.enqueueInternal();
+ }
+ }
+ }
+
+ private static class FinalizerDaemon extends Daemon {
+ private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
+ private final ReferenceQueue<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);
+ }
+ }
+
+ // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap.
+ public static void requestHeapTrim() {
+ synchronized (HeapTrimmerDaemon.INSTANCE) {
+ HeapTrimmerDaemon.INSTANCE.notify();
+ }
+ }
+
+ private static class HeapTrimmerDaemon extends Daemon {
+ private static final HeapTrimmerDaemon INSTANCE = new HeapTrimmerDaemon();
+
+ @Override public void run() {
+ while (isRunning()) {
+ try {
+ synchronized (this) {
+ wait();
+ }
+ VMRuntime.getRuntime().trimHeap();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ }
+
+ // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap.
+ public static void requestGC() {
+ GCDaemon.INSTANCE.requestGC();
+ }
+
+ private static class GCDaemon extends Daemon {
+ private static final GCDaemon INSTANCE = new GCDaemon();
+ private int count = 0;
+
+ public void requestGC() {
+ synchronized (this) {
+ ++count;
+ notify();
+ }
+ }
+
+ @Override public void run() {
+ while (isRunning()) {
+ try {
+ synchronized (this) {
+ // Wait until a request comes in, unless we have a pending request.
+ while (count == 0) {
+ wait();
+ }
+ --count;
+ }
+ VMRuntime.getRuntime().concurrentGC();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ }
+}
diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java
new file mode 100644
index 0000000..3603f9c
--- /dev/null
+++ b/libart/src/main/java/java/lang/DexCache.java
@@ -0,0 +1,97 @@
+/*
+ * 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) 2012 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 java.lang.reflect.ArtField;
+import java.lang.reflect.ArtMethod;
+
+/**
+ * A dex cache holds resolved copies of strings, fields, methods, and classes from the dexfile.
+ */
+final class DexCache {
+ /** Lazily initialized dex file wrapper. Volatile to avoid double-check locking issues. */
+ private volatile Dex dex;
+
+ /** Indexed by the type index array of locations of initialized static storage. */
+ Object[] initializedStaticStorage;
+
+ /** The location of the associated dex file. */
+ String location;
+
+ /**
+ * References to fields as they become resolved following interpreter semantics. May refer to
+ * fields defined in other dex files.
+ */
+ ArtField[] resolvedFields;
+
+ /**
+ * References to methods as they become resolved following interpreter semantics. May refer to
+ * methods defined in other dex files.
+ */
+ ArtMethod[] resolvedMethods;
+
+ /**
+ * References to types as they become resolved following interpreter semantics. May refer to
+ * types defined in other dex files.
+ */
+ Class[] resolvedTypes;
+
+ /**
+ * References to strings as they become resolved following interpreter semantics. All strings
+ * are interned.
+ */
+ String[] strings;
+
+ /** Holds C pointer to dexFile. */
+ private int dexFile;
+
+ // Only created by the VM.
+ private DexCache() {}
+
+ Dex getDex() {
+ Dex result = dex;
+ if (result == null) {
+ synchronized (this) {
+ result = dex;
+ if (result == null) {
+ dex = result = getDexNative();
+ }
+ }
+ }
+ return result;
+ }
+
+ private native Dex getDexNative();
+}
+
diff --git a/libart/src/main/java/java/lang/Enum.java b/libart/src/main/java/java/lang/Enum.java
new file mode 100644
index 0000000..f98554a
--- /dev/null
+++ b/libart/src/main/java/java/lang/Enum.java
@@ -0,0 +1,222 @@
+/* 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;
+ }
+ try {
+ Method method = enumType.getDeclaredMethod("values", EmptyArray.CLASS);
+ return (Object[]) method.invoke((Object[]) null);
+ } catch (NoSuchMethodException impossible) {
+ throw new AssertionError("impossible", impossible);
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError("impossible", impossible);
+ } catch (InvocationTargetException impossible) {
+ throw new AssertionError("impossible", impossible);
+ }
+ }
+ };
+
+ 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/libart/src/main/java/java/lang/Object.java b/libart/src/main/java/java/lang/Object.java
new file mode 100644
index 0000000..9c59870
--- /dev/null
+++ b/libart/src/main/java/java/lang/Object.java
@@ -0,0 +1,452 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+/**
+ * The root class of the Java class hierarchy. All non-primitive types
+ * (including arrays) inherit either directly or indirectly from this class.
+ *
+ * <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 {
+
+ private transient Class<?> shadow$_klass_;
+ private transient int shadow$_monitor_;
+
+ /**
+ * Constructs a new instance of {@code Object}.
+ */
+ public Object() {
+ if (shadow$_klass_.isFinalizable()) {
+ java.lang.ref.FinalizerReference.add(this);
+ }
+ }
+
+ /**
+ * Creates and returns a copy of this {@code Object}. The default
+ * implementation returns a so-called "shallow" copy: It creates a new
+ * instance of the same class and then copies the field values (including
+ * object references) from this instance to the new instance. A "deep" copy,
+ * in contrast, would also recursively clone nested objects. A subclass that
+ * needs to implement this kind of cloning should call {@code super.clone()}
+ * to create the new instance and then create deep copies of the nested,
+ * mutable objects.
+ *
+ * @return a copy of this object.
+ * @throws CloneNotSupportedException
+ * if this object's class does not implement the {@code
+ * Cloneable} interface.
+ */
+ protected Object clone() throws CloneNotSupportedException {
+ if (!(this instanceof Cloneable)) {
+ throw new CloneNotSupportedException("Class " + getClass().getName() +
+ " doesn't implement Cloneable");
+ }
+
+ return internalClone();
+ }
+
+ /*
+ * Native helper method for cloning.
+ */
+ private native Object internalClone();
+
+ /**
+ * Compares this instance with the specified object and indicates if they
+ * are equal. In order to be equal, {@code o} must represent the same object
+ * as this instance using a class-specific comparison. The general contract
+ * is that this comparison should be reflexive, symmetric, and transitive.
+ * Also, no object reference other than null is equal to null.
+ *
+ * <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 Class<?> getClass() {
+ return shadow$_klass_;
+ }
+
+ /**
+ * Returns an integer hash code for this object. By contract, any two
+ * objects for which {@link #equals} returns {@code true} must return
+ * the same hash code value. This means that subclasses of {@code Object}
+ * usually override both methods or neither method.
+ *
+ * <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 int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ /**
+ * Causes a thread which is waiting on this object's monitor (by means of
+ * calling one of the {@code wait()} methods) to be woken up. If more than
+ * one thread is waiting, one of them is chosen at the discretion of the
+ * VM. The chosen thread will not run immediately. The thread
+ * that called {@code notify()} has to release the object's monitor first.
+ * Also, the chosen thread still has to compete against other threads that
+ * try to synchronize on the same object.
+ * <p>
+ * This method can only be invoked by a thread which owns this object's
+ * monitor. A thread becomes owner of an object's monitor
+ * </p>
+ * <ul>
+ * <li>by executing a synchronized method of that object;</li>
+ * <li>by executing the body of a {@code synchronized} statement that
+ * synchronizes on the object;</li>
+ * <li>by executing a synchronized static method if the object is of type
+ * {@code Class}.</li>
+ * </ul>
+ *
+ * @see #notifyAll
+ * @see #wait()
+ * @see #wait(long)
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ */
+ public final native void notify();
+
+ /**
+ * Causes all threads which are waiting on this object's monitor (by means
+ * of calling one of the {@code wait()} methods) to be woken up. The threads
+ * will not run immediately. The thread that called {@code notify()} has to
+ * release the object's monitor first. Also, the threads still have to
+ * compete against other threads that try to synchronize on the same object.
+ * <p>
+ * This method can only be invoked by a thread which owns this object's
+ * monitor. A thread becomes owner of an object's monitor
+ * </p>
+ * <ul>
+ * <li>by executing a synchronized method of that object;</li>
+ * <li>by executing the body of a {@code synchronized} statement that
+ * synchronizes on the object;</li>
+ * <li>by executing a synchronized static method if the object is of type
+ * {@code Class}.</li>
+ * </ul>
+ *
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @see #notify
+ * @see #wait()
+ * @see #wait(long)
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ */
+ public final native void notifyAll();
+
+ /**
+ * Returns a string containing a concise, human-readable description of this
+ * object. Subclasses are encouraged to override this method and provide an
+ * implementation that takes into account the object's type and data. The
+ * default implementation is equivalent to the following expression:
+ * <pre>
+ * getClass().getName() + '@' + Integer.toHexString(hashCode())</pre>
+ * <p>See <a href="{@docRoot}reference/java/lang/Object.html#writing_toString">Writing a useful
+ * {@code toString} method</a>
+ * if you intend implementing your own {@code toString} method.
+ *
+ * @return a printable representation of this object.
+ */
+ public String toString() {
+ return getClass().getName() + '@' + Integer.toHexString(hashCode());
+ }
+
+ /**
+ * Causes the calling thread to wait until another thread calls the {@code
+ * notify()} or {@code notifyAll()} method of this object. This method can
+ * only be invoked by a thread which owns this object's monitor; see
+ * {@link #notify()} on how a thread can become the owner of a monitor.
+ * <p>
+ * A waiting thread can be sent {@code interrupt()} to cause it to
+ * prematurely stop waiting, so {@code wait} should be called in a loop to
+ * check that the condition that has been waited for has been met before
+ * continuing.
+ * </p>
+ * <p>
+ * While the thread waits, it gives up ownership of this object's monitor.
+ * When it is notified (or interrupted), it re-acquires the monitor before
+ * it starts running.
+ * </p>
+ *
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @throws InterruptedException
+ * if another thread interrupts this thread while it is waiting.
+ * @see #notify
+ * @see #notifyAll
+ * @see #wait(long)
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ */
+ public final native void wait() throws InterruptedException;
+
+ /**
+ * Causes the calling thread to wait until another thread calls the {@code
+ * notify()} or {@code notifyAll()} method of this object or until the
+ * specified timeout expires. This method can only be invoked by a thread
+ * which owns this object's monitor; see {@link #notify()} on how a thread
+ * can become the owner of a monitor.
+ * <p>
+ * A waiting thread can be sent {@code interrupt()} to cause it to
+ * prematurely stop waiting, so {@code wait} should be called in a loop to
+ * check that the condition that has been waited for has been met before
+ * continuing.
+ * </p>
+ * <p>
+ * While the thread waits, it gives up ownership of this object's monitor.
+ * When it is notified (or interrupted), it re-acquires the monitor before
+ * it starts running.
+ * </p>
+ *
+ * @param millis
+ * the maximum time to wait in milliseconds.
+ * @throws IllegalArgumentException
+ * if {@code millis < 0}.
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @throws InterruptedException
+ * if another thread interrupts this thread while it is waiting.
+ * @see #notify
+ * @see #notifyAll
+ * @see #wait()
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ */
+ public final void wait(long millis) throws InterruptedException {
+ wait(millis, 0);
+ }
+
+ /**
+ * Causes the calling thread to wait until another thread calls the {@code
+ * notify()} or {@code notifyAll()} method of this object or until the
+ * specified timeout expires. This method can only be invoked by a thread
+ * that owns this object's monitor; see {@link #notify()} on how a thread
+ * can become the owner of a monitor.
+ * <p>
+ * A waiting thread can be sent {@code interrupt()} to cause it to
+ * prematurely stop waiting, so {@code wait} should be called in a loop to
+ * check that the condition that has been waited for has been met before
+ * continuing.
+ * </p>
+ * <p>
+ * While the thread waits, it gives up ownership of this object's monitor.
+ * When it is notified (or interrupted), it re-acquires the monitor before
+ * it starts running.
+ * </p>
+ *
+ * @param millis
+ * the maximum time to wait in milliseconds.
+ * @param nanos
+ * the fraction of a millisecond to wait, specified in
+ * nanoseconds.
+ * @throws IllegalArgumentException
+ * if {@code millis < 0}, {@code nanos < 0} or {@code nanos >
+ * 999999}.
+ * @throws IllegalMonitorStateException
+ * if the thread calling this method is not the owner of this
+ * object's monitor.
+ * @throws InterruptedException
+ * if another thread interrupts this thread while it is waiting.
+ * @see #notify
+ * @see #notifyAll
+ * @see #wait()
+ * @see #wait(long,int)
+ * @see java.lang.Thread
+ */
+ public final native void wait(long millis, int nanos) throws InterruptedException;
+}
diff --git a/libart/src/main/java/java/lang/String.java b/libart/src/main/java/java/lang/String.java
new file mode 100644
index 0000000..385f549
--- /dev/null
+++ b/libart/src/main/java/java/lang/String.java
@@ -0,0 +1,2011 @@
+/*
+ * 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
+ *
+ * @exception 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Returns the character at the specified offset in this string.
+ *
+ * @param index
+ * the zero-based index in this string.
+ * @return the character at the 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)}
+ */
+ @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/libart/src/main/java/java/lang/Thread.java b/libart/src/main/java/java/lang/Thread.java
new file mode 100644
index 0000000..5c81e36
--- /dev/null
+++ b/libart/src/main/java/java/lang/Thread.java
@@ -0,0 +1,1267 @@
+/*
+ * 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 */
+ private volatile int nativePeer;
+ 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's join/sleep/park operations.
+ */
+ private final Object lock = new Object();
+
+ /** Looked up reflectively and used by java.util.concurrent.locks.LockSupport. */
+ private Object parkBlocker;
+
+ /**
+ * Constructs a new {@code Thread} with no {@code Runnable} object and a
+ * newly generated name. The new {@code Thread} will belong to the same
+ * {@code ThreadGroup} as the {@code Thread} calling this constructor.
+ *
+ * @see java.lang.ThreadGroup
+ * @see java.lang.Runnable
+ */
+ public Thread() {
+ create(null, null, null, 0);
+ }
+
+ /**
+ * Constructs a new {@code Thread} with a {@code Runnable} object and a
+ * newly generated name. The new {@code Thread} will belong to the same
+ * {@code ThreadGroup} as the {@code Thread} calling this constructor.
+ *
+ * @param runnable
+ * a {@code Runnable} whose method <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 == null");
+ }
+
+ this.group = group;
+
+ this.target = null;
+ this.stackSize = 0;
+ this.priority = priority;
+ this.daemon = daemon;
+
+ /* add ourselves to our ThreadGroup of choice */
+ this.group.addThread(this);
+ }
+
+ /**
+ * Initializes a new, existing Thread object with a runnable object,
+ * the given name and belonging to the ThreadGroup passed as parameter.
+ * This is the method that the several public constructors delegate their
+ * work to.
+ *
+ * @param group ThreadGroup to which the new Thread will belong
+ * @param runnable a java.lang.Runnable whose method <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.
+ */
+ public static native Thread currentThread();
+
+ /**
+ * Throws {@code UnsupportedOperationException}.
+ * @deprecated Not implemented.
+ */
+ @Deprecated
+ public void destroy() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Prints to the standard error stream a text representation of the current
+ * stack for this Thread.
+ *
+ * @see Throwable#printStackTrace()
+ */
+ public static void dumpStack() {
+ new Throwable("stack dump").printStackTrace();
+ }
+
+ /**
+ * Copies an array with all Threads which are in the same ThreadGroup as the
+ * receiver - and subgroups - into the array <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.systemThreadGroup.activeCount();
+ Thread[] threads = new Thread[count + count / 2];
+
+ // Enumerate the threads and collect the stacktraces.
+ count = ThreadGroup.systemThreadGroup.enumerate(threads);
+ for (int i = 0; i < count; i++) {
+ map.put(threads[i], threads[i].getStackTrace());
+ }
+
+ return map;
+ }
+
+ /**
+ * Returns the context ClassLoader for this Thread.
+ *
+ * @return ClassLoader The context ClassLoader
+ * @see java.lang.ClassLoader
+ * @see #getContextClassLoader()
+ */
+ public ClassLoader getContextClassLoader() {
+ return contextClassLoader;
+ }
+
+ /**
+ * Returns the default exception handler that's executed when uncaught
+ * exception terminates a thread.
+ *
+ * @return an {@link UncaughtExceptionHandler} or <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() {
+ return State.values()[nativeGetStatus(hasBeenStarted)];
+ }
+
+ private native int nativeGetStatus(boolean hasBeenStarted);
+
+ /**
+ * Returns the ThreadGroup to which this Thread belongs.
+ *
+ * @return the Thread's ThreadGroup
+ */
+ public final ThreadGroup getThreadGroup() {
+ // TODO This should actually be done at native termination.
+ if (getState() == Thread.State.TERMINATED) {
+ return null;
+ } else {
+ return group;
+ }
+ }
+
+ /**
+ * Returns the thread's uncaught exception handler. If not explicitly set,
+ * then the ThreadGroup's handler is returned. If the thread is terminated,
+ * then <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.
+ nativeInterrupt();
+
+ synchronized (interruptActions) {
+ for (int i = interruptActions.size() - 1; i >= 0; i--) {
+ interruptActions.get(i).run();
+ }
+ }
+ }
+
+ private native void nativeInterrupt();
+
+ /**
+ * 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 native boolean 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 (nativePeer != 0);
+ }
+
+ /**
+ * 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 native boolean isInterrupted();
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies.
+ *
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ */
+ public final void join() throws InterruptedException {
+ synchronized (lock) {
+ while (isAlive()) {
+ lock.wait();
+ }
+ }
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies or the specified timeout
+ * expires, whatever happens first.
+ *
+ * @param millis The maximum time to wait (in milliseconds).
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ */
+ public final void join(long millis) throws InterruptedException {
+ join(millis, 0);
+ }
+
+ /**
+ * Blocks the current Thread (<code>Thread.currentThread()</code>) until
+ * the receiver finishes its execution and dies or the specified timeout
+ * expires, whatever happens first.
+ *
+ * @param millis The maximum time to wait (in milliseconds).
+ * @param nanos Extra nanosecond precision
+ * @throws InterruptedException if <code>interrupt()</code> was called for
+ * the receiver while it was in the <code>join()</code> call
+ * @see Object#notifyAll
+ * @see java.lang.ThreadDeath
+ */
+ public final void join(long millis, int nanos) throws InterruptedException {
+ if (millis < 0 || nanos < 0 || nanos >= NANOS_PER_MILLI) {
+ throw new IllegalArgumentException("bad timeout: millis=" + millis + ",nanos=" + nanos);
+ }
+
+ // avoid overflow: if total > 292,277 years, just wait forever
+ boolean overflow = millis >= (Long.MAX_VALUE - nanos) / NANOS_PER_MILLI;
+ boolean forever = (millis | nanos) == 0;
+ if (forever | overflow) {
+ join();
+ return;
+ }
+
+ synchronized (lock) {
+ 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) {
+ lock.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 (nativePeer == 0) {
+ 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");
+ }
+ // The lock is taken to ensure no race occurs between starting the
+ // the thread and setting its name (and the name of its native peer).
+ synchronized (this) {
+ this.name = threadName;
+
+ if (isAlive()) {
+ nativeSetName(threadName);
+ }
+ }
+ }
+
+ /**
+ * Tell the VM that the thread's name has changed. This is useful for
+ * DDMS, which would otherwise be oblivious to Thread.setName calls.
+ */
+ private native void nativeSetName(String newName);
+
+ /**
+ * 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();
+ }
+
+ // The lock is taken to ensure no race occurs between starting the
+ // the thread and setting its priority (and the priority of its native peer).
+ synchronized (this) {
+ this.priority = priority;
+
+ if (isAlive()) {
+ nativeSetPriority(priority);
+ }
+ }
+ }
+
+ private native void nativeSetPriority(int newPriority);
+
+ /**
+ * <p>
+ * Sets the uncaught exception handler. This handler is invoked in case this
+ * Thread dies due to an unhandled exception.
+ * </p>
+ *
+ * @param handler
+ * The handler to set or <code>null</code>.
+ */
+ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
+ uncaughtHandler = handler;
+ }
+
+ /**
+ * Causes the thread which sent this message to sleep for the given interval
+ * of time (given in milliseconds). The precision is not guaranteed - the
+ * Thread may sleep more or less than requested.
+ *
+ * @param time
+ * The time to sleep in milliseconds.
+ * @throws InterruptedException
+ * if <code>interrupt()</code> was called for this Thread while
+ * it was sleeping
+ * @see Thread#interrupt()
+ */
+ public static void sleep(long time) throws InterruptedException {
+ Thread.sleep(time, 0);
+ }
+
+ /**
+ * Causes the thread which sent this message to sleep for the given interval
+ * of time (given in milliseconds and nanoseconds). The precision is not
+ * guaranteed - the Thread may sleep more or less than requested.
+ *
+ * @param millis
+ * The time to sleep in milliseconds.
+ * @param nanos
+ * Extra nanosecond precision
+ * @throws InterruptedException
+ * if <code>interrupt()</code> was called for this Thread while
+ * it was sleeping
+ * @see Thread#interrupt()
+ */
+ public static void sleep(long millis, int nanos) throws InterruptedException {
+ // The JLS 3rd edition, section 17.9 says: "...sleep for zero
+ // time...need not have observable effects."
+ if (millis == 0 && nanos == 0) {
+ return;
+ }
+
+ long start = System.nanoTime();
+ long duration = (millis * NANOS_PER_MILLI) + nanos;
+
+ Object lock = currentThread().lock;
+
+ // Wait may return early, so loop until sleep duration passes.
+ synchronized (lock) {
+ while (true) {
+ sleep(lock, millis, nanos);
+
+ long now = System.nanoTime();
+ long elapsed = now - start;
+
+ if (elapsed >= duration) {
+ break;
+ }
+
+ duration -= elapsed;
+ start = now;
+ millis = duration / NANOS_PER_MILLI;
+ nanos = (int) (duration % NANOS_PER_MILLI);
+ }
+ }
+ }
+
+ private static native void sleep(Object lock, long millis, int 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;
+
+ nativeCreate(this, stackSize, daemon);
+ }
+
+ private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
+
+ /**
+ * Requests the receiver Thread to stop and throw ThreadDeath. The Thread is
+ * resumed if it was suspended and awakened if it was sleeping, so that it
+ * can proceed to throw ThreadDeath.
+ *
+ * @deprecated because stopping a thread in this manner is unsafe and can
+ * leave your application and the VM in an unpredictable state.
+ */
+ @Deprecated
+ public final void stop() {
+ stop(new ThreadDeath());
+ }
+
+ /**
+ * Throws {@code UnsupportedOperationException}.
+ * @deprecated because stopping a thread in this manner is unsafe and can
+ * leave your application and the VM in an unpredictable state.
+ */
+ @Deprecated
+ public final synchronized void stop(Throwable throwable) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Throws {@code UnsupportedOperationException}.
+ * @deprecated May cause deadlocks.
+ */
+ @Deprecated
+ public final void suspend() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns a string containing a concise, human-readable description of the
+ * Thread. It includes the Thread's name, priority, and group name.
+ *
+ * @return a printable representation for the receiver.
+ */
+ @Override
+ public String toString() {
+ return "Thread[" + name + "," + priority + "," + group.getName() + "]";
+ }
+
+ /**
+ * Causes the calling Thread to yield execution time to another Thread that
+ * is ready to run. The actual scheduling is implementation-dependent.
+ */
+ public static native void 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().nativeHoldsLock(object);
+ }
+
+ private native boolean nativeHoldsLock(Object 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() {
+ synchronized (lock) {
+ 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;
+ lock.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) {
+ synchronized (lock) {
+ 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 {
+ lock.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("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) {
+ synchronized (lock) {
+ /*
+ * 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/libart/src/main/java/java/lang/ThreadGroup.java b/libart/src/main/java/java/lang/ThreadGroup.java
new file mode 100644
index 0000000..51b2137
--- /dev/null
+++ b/libart/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 systemThreadGroup = new ThreadGroup();
+ static final ThreadGroup mainThreadGroup = new ThreadGroup(systemThreadGroup, "main");
+
+ /**
+ * Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup}
+ * will be child of the {@code ThreadGroup} to which the calling thread belongs.
+ *
+ * @param name the name
+ * @see Thread#currentThread
+ */
+ public ThreadGroup(String name) {
+ this(Thread.currentThread().getThreadGroup(), name);
+ }
+
+ /**
+ * Constructs a new {@code ThreadGroup} with the given name, as a child of the
+ * given {@code ThreadGroup}.
+ *
+ * @param parent the parent
+ * @param name the name
+ * @throws NullPointerException if {@code parent == null}
+ * @throws IllegalThreadStateException if {@code parent} has been
+ * destroyed already
+ */
+ public ThreadGroup(ThreadGroup parent, String name) {
+ if (parent == null) {
+ throw new NullPointerException("parent == null");
+ }
+ this.name = name;
+ this.parent = parent;
+ if (parent != null) {
+ parent.add(this);
+ this.setMaxPriority(parent.getMaxPriority());
+ if (parent.isDaemon()) {
+ this.setDaemon(true);
+ }
+ }
+ }
+
+ /**
+ * Initialize the special "system" ThreadGroup. Was "main" in Harmony,
+ * but we have an additional group above that in Android.
+ */
+ private ThreadGroup() {
+ this.name = "system";
+ this.parent = null;
+ }
+
+ /**
+ * Returns the number of running {@code Thread}s which are children of this thread group,
+ * directly or indirectly.
+ *
+ * @return the number of children
+ */
+ public int activeCount() {
+ int count = 0;
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ if (thread.isAlive()) {
+ count++;
+ }
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ count += group.activeCount();
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns the number of {@code ThreadGroup}s which are children of this group,
+ * directly or indirectly.
+ *
+ * @return the number of children
+ */
+ public int activeGroupCount() {
+ int count = 0;
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ // One for this group & the subgroups
+ count += 1 + group.activeGroupCount();
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Adds a {@code ThreadGroup} to this thread group.
+ *
+ * @param g ThreadGroup to add
+ * @throws IllegalThreadStateException if this group has been destroyed already
+ */
+ private void add(ThreadGroup g) throws IllegalThreadStateException {
+ synchronized (groups) {
+ if (isDestroyed) {
+ throw new IllegalThreadStateException();
+ }
+ groups.add(g);
+ }
+ }
+
+ /**
+ * Does nothing. The definition of this method depends on the deprecated
+ * method {@link #suspend()}. The exact behavior of this call was never
+ * specified.
+ *
+ * @param b Used to control low memory implicit suspension
+ * @return {@code true} (always)
+ *
+ * @deprecated Required deprecated method suspend().
+ */
+ @Deprecated
+ public boolean allowThreadSuspension(boolean b) {
+ // Does not apply to this VM, no-op
+ return true;
+ }
+
+ /**
+ * Does nothing.
+ */
+ public final void checkAccess() {
+ }
+
+ /**
+ * Destroys this thread group and recursively all its subgroups. It is only legal
+ * to destroy a {@code ThreadGroup} that has no threads in it. Any daemon
+ * {@code ThreadGroup} is destroyed automatically when it becomes empty (no threads
+ * or thread groups in it).
+ *
+ * @throws IllegalThreadStateException if this thread group or any of its
+ * subgroups has been destroyed already or if it still contains
+ * threads.
+ */
+ public final void destroy() {
+ synchronized (threadRefs) {
+ synchronized (groups) {
+ if (isDestroyed) {
+ throw new IllegalThreadStateException(
+ "Thread group was already destroyed: "
+ + (this.name != null ? this.name : "n/a"));
+ }
+ if (threads.iterator().hasNext()) {
+ throw new IllegalThreadStateException(
+ "Thread group still contains threads: "
+ + (this.name != null ? this.name : "n/a"));
+ }
+ // Call recursively for subgroups
+ while (!groups.isEmpty()) {
+ // We always get the first element - remember, when the
+ // child dies it removes itself from our collection. See
+ // below.
+ groups.get(0).destroy();
+ }
+
+ if (parent != null) {
+ parent.remove(this);
+ }
+
+ // Now that the ThreadGroup is really destroyed it can be tagged as so
+ this.isDestroyed = true;
+ }
+ }
+ }
+
+ /*
+ * Auxiliary method that destroys this thread group and recursively all its
+ * subgroups if this is a daemon ThreadGroup.
+ *
+ * @see #destroy
+ * @see #setDaemon
+ * @see #isDaemon
+ */
+ private void destroyIfEmptyDaemon() {
+ // Has to be non-destroyed daemon to make sense
+ synchronized (threadRefs) {
+ if (isDaemon && !isDestroyed && !threads.iterator().hasNext()) {
+ synchronized (groups) {
+ if (groups.isEmpty()) {
+ destroy();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates over all active threads in this group (and its sub-groups) and
+ * stores the threads in the given array. Returns when the array is full or
+ * no more threads remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any threads that don't fit in the
+ * supplied array.
+ *
+ * @param threads the array into which the {@code Thread}s will be copied
+ * @return the number of {@code Thread}s that were copied
+ */
+ public int enumerate(Thread[] threads) {
+ return enumerate(threads, true);
+ }
+
+ /**
+ * Iterates over all active threads in this group (and, optionally, its
+ * sub-groups) and stores the threads in the given array. Returns when the
+ * array is full or no more threads remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any threads that don't fit in the
+ * supplied array.
+ *
+ * @param threads the array into which the {@code Thread}s will be copied
+ * @param recurse indicates whether {@code Thread}s in subgroups should be
+ * recursively copied as well
+ * @return the number of {@code Thread}s that were copied
+ */
+ public int enumerate(Thread[] threads, boolean recurse) {
+ return enumerateGeneric(threads, recurse, 0, true);
+ }
+
+ /**
+ * Iterates over all thread groups in this group (and its sub-groups) and
+ * and stores the groups in the given array. Returns when the array is full
+ * or no more groups remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any thread groups that don't fit in the
+ * supplied array.
+ *
+ * @param groups the array into which the {@code ThreadGroup}s will be copied
+ * @return the number of {@code ThreadGroup}s that were copied
+ */
+ public int enumerate(ThreadGroup[] groups) {
+ return enumerate(groups, true);
+ }
+
+ /**
+ * Iterates over all thread groups in this group (and, optionally, its
+ * sub-groups) and stores the groups in the given array. Returns when
+ * the array is full or no more groups remain, whichever happens first.
+ *
+ * <p>Note that this method will silently ignore any thread groups that don't fit in the
+ * supplied array.
+ *
+ * @param groups the array into which the {@code ThreadGroup}s will be copied
+ * @param recurse indicates whether {@code ThreadGroup}s in subgroups should be
+ * recursively copied as well or not
+ * @return the number of {@code ThreadGroup}s that were copied
+ */
+ public int enumerate(ThreadGroup[] groups, boolean recurse) {
+ return enumerateGeneric(groups, recurse, 0, false);
+ }
+
+ /**
+ * Copies into <param>enumeration</param> starting at
+ * <param>enumerationIndex</param> all Threads or ThreadGroups in the
+ * receiver. If <param>recurse</param> is true, recursively enumerate the
+ * elements in subgroups.
+ *
+ * If the array passed as parameter is too small no exception is thrown -
+ * the extra elements are simply not copied.
+ *
+ * @param enumeration array into which the elements will be copied
+ * @param recurse Indicates whether subgroups should be enumerated or not
+ * @param enumerationIndex Indicates in which position of the enumeration
+ * array we are
+ * @param enumeratingThreads Indicates whether we are enumerating Threads or
+ * ThreadGroups
+ * @return How many elements were enumerated/copied over
+ */
+ private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex,
+ boolean enumeratingThreads) {
+ if (enumeratingThreads) {
+ synchronized (threadRefs) {
+ // walk the references directly so we can iterate in reverse order
+ for (int i = threadRefs.size() - 1; i >= 0; --i) {
+ Thread thread = threadRefs.get(i).get();
+ if (thread != null && thread.isAlive()) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumeration[enumerationIndex++] = thread;
+ }
+ }
+ }
+ } else {
+ synchronized (groups) {
+ for (int i = groups.size() - 1; i >= 0; --i) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumeration[enumerationIndex++] = groups.get(i);
+ }
+ }
+ }
+
+ if (recurse) {
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ if (enumerationIndex >= enumeration.length) {
+ return enumerationIndex;
+ }
+ enumerationIndex = group.enumerateGeneric(enumeration, recurse,
+ enumerationIndex, enumeratingThreads);
+ }
+ }
+ }
+ return enumerationIndex;
+ }
+
+ /**
+ * Returns the maximum allowed priority for a {@code Thread} in this thread group.
+ *
+ * @return the maximum priority
+ *
+ * @see #setMaxPriority
+ */
+ public final int getMaxPriority() {
+ return maxPriority;
+ }
+
+ /**
+ * Returns the name of this thread group.
+ *
+ * @return the group's name
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * Returns this thread group's parent {@code ThreadGroup}. It can be null if this
+ * is the the root ThreadGroup.
+ *
+ * @return the parent
+ */
+ public final ThreadGroup getParent() {
+ return parent;
+ }
+
+ /**
+ * Interrupts every {@code Thread} in this group and recursively in all its
+ * subgroups.
+ *
+ * @see Thread#interrupt
+ */
+ public final void interrupt() {
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ thread.interrupt();
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.interrupt();
+ }
+ }
+ }
+
+ /**
+ * Checks whether this thread group is a daemon {@code ThreadGroup}.
+ *
+ * @return true if this thread group is a daemon {@code ThreadGroup}
+ *
+ * @see #setDaemon
+ * @see #destroy
+ */
+ public final boolean isDaemon() {
+ return isDaemon;
+ }
+
+ /**
+ * Checks whether this thread group has already been destroyed.
+ *
+ * @return true if this thread group has already been destroyed
+ * @see #destroy
+ */
+ public synchronized boolean isDestroyed() {
+ return isDestroyed;
+ }
+
+ /**
+ * Outputs to {@code System.out} a text representation of the
+ * hierarchy of {@code Thread}s and {@code ThreadGroup}s in this thread group (and recursively).
+ * Proper indentation is used to show the nesting of groups inside groups
+ * and threads inside groups.
+ */
+ public void list() {
+ // We start in a fresh line
+ System.out.println();
+ list(0);
+ }
+
+ /*
+ * Outputs to {@code System.out}a text representation of the
+ * hierarchy of Threads and ThreadGroups in this thread group (and recursively).
+ * The indentation will be four spaces per level of nesting.
+ *
+ * @param levels How many levels of nesting, so that proper indentation can
+ * be output.
+ */
+ private void list(int levels) {
+ indent(levels);
+ System.out.println(this.toString());
+
+ ++levels;
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ indent(levels);
+ System.out.println(thread);
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.list(levels);
+ }
+ }
+ }
+
+ private void indent(int levels) {
+ for (int i = 0; i < levels; i++) {
+ System.out.print(" "); // 4 spaces for each level
+ }
+ }
+
+ /**
+ * Checks whether this thread group is a direct or indirect parent group of a
+ * given {@code ThreadGroup}.
+ *
+ * @param g the potential child {@code ThreadGroup}
+ * @return true if this thread group is parent of {@code g}
+ */
+ public final boolean parentOf(ThreadGroup g) {
+ while (g != null) {
+ if (this == g) {
+ return true;
+ }
+ g = g.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Removes an immediate subgroup.
+ *
+ * @param g ThreadGroup to remove
+ *
+ * @see #add(Thread)
+ * @see #add(ThreadGroup)
+ */
+ private void remove(ThreadGroup g) {
+ synchronized (groups) {
+ for (Iterator<ThreadGroup> i = groups.iterator(); i.hasNext(); ) {
+ ThreadGroup threadGroup = i.next();
+ if (threadGroup.equals(g)) {
+ i.remove();
+ break;
+ }
+ }
+ }
+ destroyIfEmptyDaemon();
+ }
+
+ /**
+ * Resumes every thread in this group and recursively in all its
+ * subgroups.
+ *
+ * @see Thread#resume
+ * @see #suspend
+ *
+ * @deprecated Requires deprecated method Thread.resume().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void resume() {
+ synchronized (threadRefs) {
+ for (Thread thread : threads) {
+ thread.resume();
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.resume();
+ }
+ }
+ }
+
+ /**
+ * Sets whether this is a daemon {@code ThreadGroup} or not. Daemon
+ * thread groups are automatically destroyed when they become empty.
+ *
+ * @param isDaemon the new value
+ * @see #isDaemon
+ * @see #destroy
+ */
+ public final void setDaemon(boolean isDaemon) {
+ this.isDaemon = isDaemon;
+ }
+
+ /**
+ * Configures the maximum allowed priority for a {@code Thread} in this group and
+ * recursively in all its subgroups.
+ *
+ * <p>A caller can never increase the maximum priority of a thread group.
+ * Such an attempt will not result in an exception, it will
+ * simply leave the thread group with its current maximum priority.
+ *
+ * @param newMax the new maximum priority to be set
+ *
+ * @throws IllegalArgumentException if the new priority is greater than
+ * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY
+ *
+ * @see #getMaxPriority
+ */
+ public final void setMaxPriority(int newMax) {
+ if (newMax <= this.maxPriority) {
+ if (newMax < Thread.MIN_PRIORITY) {
+ newMax = Thread.MIN_PRIORITY;
+ }
+
+ int parentPriority = parent == null ? newMax : parent.getMaxPriority();
+ this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ group.setMaxPriority(newMax);
+ }
+ }
+ }
+ }
+
+ /**
+ * Stops every thread in this group and recursively in all its subgroups.
+ *
+ * @see Thread#stop()
+ * @see Thread#stop(Throwable)
+ * @see ThreadDeath
+ *
+ * @deprecated Requires deprecated method Thread.stop().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void stop() {
+ if (stopHelper()) {
+ Thread.currentThread().stop();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean stopHelper() {
+ boolean stopCurrent = false;
+ synchronized (threadRefs) {
+ Thread current = Thread.currentThread();
+ for (Thread thread : threads) {
+ if (thread == current) {
+ stopCurrent = true;
+ } else {
+ thread.stop();
+ }
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ stopCurrent |= group.stopHelper();
+ }
+ }
+ return stopCurrent;
+ }
+
+ /**
+ * Suspends every thread in this group and recursively in all its
+ * subgroups.
+ *
+ * @see Thread#suspend
+ * @see #resume
+ *
+ * @deprecated Requires deprecated method Thread.suspend().
+ */
+ @SuppressWarnings("deprecation")
+ @Deprecated
+ public final void suspend() {
+ if (suspendHelper()) {
+ Thread.currentThread().suspend();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean suspendHelper() {
+ boolean suspendCurrent = false;
+ synchronized (threadRefs) {
+ Thread current = Thread.currentThread();
+ for (Thread thread : threads) {
+ if (thread == current) {
+ suspendCurrent = true;
+ } else {
+ thread.suspend();
+ }
+ }
+ }
+ synchronized (groups) {
+ for (ThreadGroup group : groups) {
+ suspendCurrent |= group.suspendHelper();
+ }
+ }
+ return suspendCurrent;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[name=" + getName()
+ + ",maxPriority=" + getMaxPriority() + "]";
+ }
+
+ /**
+ * Handles uncaught exceptions. Any uncaught exception in any {@code Thread}
+ * is forwarded to the thread's {@code ThreadGroup} by invoking this
+ * method.
+ *
+ * <p>New code should use {@link Thread#setUncaughtExceptionHandler} instead of thread groups.
+ *
+ * @param t the Thread that terminated with an uncaught exception
+ * @param e the uncaught exception itself
+ */
+ public void uncaughtException(Thread t, Throwable e) {
+ if (parent != null) {
+ parent.uncaughtException(t, e);
+ } else if (Thread.getDefaultUncaughtExceptionHandler() != null) {
+ // TODO The spec is unclear regarding this. What do we do?
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e);
+ } else if (!(e instanceof ThreadDeath)) {
+ // No parent group, has to be 'system' Thread Group
+ e.printStackTrace(System.err);
+ }
+ }
+
+ /**
+ * Called by the Thread constructor.
+ */
+ final void addThread(Thread thread) throws IllegalThreadStateException {
+ synchronized (threadRefs) {
+ if (isDestroyed) {
+ throw new IllegalThreadStateException();
+ }
+ threadRefs.add(new WeakReference<Thread>(thread));
+ }
+ }
+
+ /**
+ * Called by the VM when a Thread dies.
+ */
+ final void removeThread(Thread thread) throws IllegalThreadStateException {
+ synchronized (threadRefs) {
+ for (Iterator<Thread> i = threads.iterator(); i.hasNext(); ) {
+ if (i.next().equals(thread)) {
+ i.remove();
+ break;
+ }
+ }
+ }
+ destroyIfEmptyDaemon();
+ }
+}
diff --git a/libart/src/main/java/java/lang/VMClassLoader.java b/libart/src/main/java/java/lang/VMClassLoader.java
new file mode 100644
index 0000000..d180a4d
--- /dev/null
+++ b/libart/src/main/java/java/lang/VMClassLoader.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+ }
+
+ 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/libart/src/main/java/java/lang/reflect/AbstractMethod.java b/libart/src/main/java/java/lang/reflect/AbstractMethod.java
new file mode 100644
index 0000000..ff52b41
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/AbstractMethod.java
@@ -0,0 +1,294 @@
+/*
+ * 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) 2012 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 com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+import libcore.reflect.GenericSignatureParser;
+import libcore.reflect.ListOfTypes;
+import libcore.reflect.Types;
+
+/**
+ * This class represents an abstract method. Abstract methods are either methods or constructors.
+ * @hide
+ */
+public abstract class AbstractMethod extends AccessibleObject {
+
+ protected final ArtMethod artMethod;
+
+ protected AbstractMethod(ArtMethod artMethod) {
+ if (artMethod == null) {
+ throw new NullPointerException("artMethod == null");
+ }
+ this.artMethod = artMethod;
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+ return super.getAnnotation(annotationClass);
+ }
+
+ /**
+ * We insert native method stubs for abstract methods so we don't have to
+ * check the access flags at the time of the method call. This results in
+ * "native abstract" methods, which can't exist. If we see the "abstract"
+ * flag set, clear the "native" flag.
+ *
+ * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+ * position, because the callers of this function are trying to convey
+ * the "traditional" meaning of the flags to their callers.
+ */
+ private static int fixMethodFlags(int flags) {
+ if ((flags & Modifier.ABSTRACT) != 0) {
+ flags &= ~Modifier.NATIVE;
+ }
+ flags &= ~Modifier.SYNCHRONIZED;
+ int ACC_DECLARED_SYNCHRONIZED = 0x00020000;
+ if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+ flags |= Modifier.SYNCHRONIZED;
+ }
+ return flags & 0xffff; // mask out bits not used by Java
+ }
+
+ int getModifiers() {
+ return fixMethodFlags(artMethod.getAccessFlags());
+ }
+
+ boolean isVarArgs() {
+ return (artMethod.getAccessFlags() & Modifier.VARARGS) != 0;
+ }
+
+ boolean isBridge() {
+ return (artMethod.getAccessFlags() & Modifier.BRIDGE) != 0;
+ }
+
+ boolean isSynthetic() {
+ return (artMethod.getAccessFlags() & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * @hide
+ */
+ public final int getAccessFlags() {
+ return artMethod.getAccessFlags();
+ }
+
+ /**
+ * Returns the class that declares this constructor or method.
+ */
+ Class<?> getDeclaringClass() {
+ return artMethod.getDeclaringClass();
+ }
+
+ /**
+ * Returns the index of this method's ID in its dex file.
+ *
+ * @hide
+ */
+ public final int getDexMethodIndex() {
+ return artMethod.getDexMethodIndex();
+ }
+
+ /**
+ * Returns the name of the method or constructor represented by this
+ * instance.
+ *
+ * @return the name of this method
+ */
+ abstract public String getName();
+
+ /**
+ * Returns an array of {@code Class} objects associated with the parameter types of this
+ * abstract method. If the method was declared with no parameters, an
+ * empty array will be returned.
+ *
+ * @return the parameter types
+ */
+ public Class<?>[] getParameterTypes() {
+ return artMethod.getParameterTypes();
+ }
+
+ /**
+ * Returns true if {@code other} has the same declaring class, name,
+ * parameters and return type as this method.
+ */
+ @Override public boolean equals(Object other) {
+ if (!(other instanceof AbstractMethod)) {
+ return false;
+ }
+ // exactly one instance of each member in this runtime
+ return this.artMethod == ((AbstractMethod) other).artMethod;
+ }
+
+ String toGenericString() {
+ return toGenericStringHelper();
+ }
+
+ Type[] getGenericParameterTypes() {
+ return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericParameterTypes, false);
+ }
+
+ Type[] getGenericExceptionTypes() {
+ return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericExceptionTypes, false);
+ }
+
+ @Override public Annotation[] getDeclaredAnnotations() {
+ List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+ return result.toArray(new Annotation[result.size()]);
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+ }
+
+ public Annotation[] getAnnotations() {
+ return super.getAnnotations();
+ }
+
+ /**
+ * 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 abstract Annotation[][] getParameterAnnotations();
+
+ /**
+ * 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")
+ abstract String getSignature();
+
+ static final class GenericInfo {
+ final ListOfTypes genericExceptionTypes;
+ final ListOfTypes genericParameterTypes;
+ final Type genericReturnType;
+ final TypeVariable<?>[] formalTypeParameters;
+
+ GenericInfo(ListOfTypes exceptions, ListOfTypes parameters, Type ret,
+ TypeVariable<?>[] formal) {
+ genericExceptionTypes = exceptions;
+ genericParameterTypes = parameters;
+ genericReturnType = ret;
+ formalTypeParameters = formal;
+ }
+ }
+
+ /**
+ * Returns generic information associated with this method/constructor member.
+ */
+ final GenericInfo getMethodOrConstructorGenericInfo() {
+ String signatureAttribute = AnnotationAccess.getSignature(this);
+ Member member;
+ Class<?>[] exceptionTypes;
+ boolean method = this instanceof Method;
+ if (method) {
+ Method m = (Method) this;
+ member = m;
+ exceptionTypes = m.getExceptionTypes();
+ } else {
+ Constructor<?> c = (Constructor<?>) this;
+ member = c;
+ exceptionTypes = c.getExceptionTypes();
+ }
+ GenericSignatureParser parser =
+ new GenericSignatureParser(member.getDeclaringClass().getClassLoader());
+ if (method) {
+ parser.parseForMethod((GenericDeclaration) this, signatureAttribute, exceptionTypes);
+ } else {
+ parser.parseForConstructor((GenericDeclaration) this,
+ signatureAttribute,
+ exceptionTypes);
+ }
+ return new GenericInfo(parser.exceptionTypes, parser.parameterTypes,
+ parser.returnType, parser.formalTypeParameters);
+ }
+
+ /**
+ * Helper for Method and Constructor for toGenericString
+ */
+ final String toGenericStringHelper() {
+ StringBuilder sb = new StringBuilder(80);
+ GenericInfo info = getMethodOrConstructorGenericInfo();
+ int modifiers = ((Member)this).getModifiers();
+ // append modifiers if any
+ if (modifiers != 0) {
+ sb.append(Modifier.toString(modifiers & ~Modifier.VARARGS)).append(' ');
+ }
+ // append type parameters
+ if (info.formalTypeParameters != null && info.formalTypeParameters.length > 0) {
+ sb.append('<');
+ for (int i = 0; i < info.formalTypeParameters.length; i++) {
+ Types.appendGenericType(sb, info.formalTypeParameters[i]);
+ if (i < info.formalTypeParameters.length - 1) {
+ sb.append(",");
+ }
+ }
+ sb.append("> ");
+ }
+ Class<?> declaringClass = ((Member) this).getDeclaringClass();
+ if (this instanceof Constructor) {
+ // append constructor name
+ Types.appendTypeName(sb, declaringClass);
+ } else {
+ // append return type
+ Types.appendGenericType(sb, Types.getType(info.genericReturnType));
+ sb.append(' ');
+ // append method name
+ Types.appendTypeName(sb, declaringClass);
+ sb.append(".").append(((Method) this).getName());
+ }
+ // append parameters
+ sb.append('(');
+ Types.appendArrayGenericType(sb, info.genericParameterTypes.getResolvedTypes());
+ sb.append(')');
+ // append exceptions if any
+ Type[] genericExceptionTypeArray =
+ Types.getTypeArray(info.genericExceptionTypes, false);
+ if (genericExceptionTypeArray.length > 0) {
+ sb.append(" throws ");
+ Types.appendArrayGenericType(sb, genericExceptionTypeArray);
+ }
+ return sb.toString();
+ }
+}
diff --git a/libart/src/main/java/java/lang/reflect/AccessibleObject.java b/libart/src/main/java/java/lang/reflect/AccessibleObject.java
new file mode 100644
index 0000000..dd57a12
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+/**
+ * {@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 {
+ protected AccessibleObject() {
+ }
+
+ /**
+ * If true, object is accessible, bypassing normal access checks
+ */
+ private boolean flag = false;
+
+ /**
+ * Returns true if this object is accessible without access checks.
+ */
+ public boolean isAccessible() {
+ return flag;
+ }
+
+ /**
+ * Attempts to set the accessible flag. Setting this to true prevents {@code
+ * IllegalAccessExceptions}.
+ */
+ public void setAccessible(boolean flag) {
+ this.flag = flag;
+ }
+
+ /**
+ * Attempts to set the accessible flag for all objects in {@code objects}.
+ * Setting this to true prevents {@code IllegalAccessExceptions}.
+ */
+ public static void setAccessible(AccessibleObject[] objects, boolean flag) {
+ for (AccessibleObject object : objects) {
+ object.flag = flag;
+ }
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public Annotation[] getDeclaredAnnotations() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public Annotation[] getAnnotations() {
+ // for all but Class, getAnnotations == getDeclaredAnnotations
+ return getDeclaredAnnotations();
+ }
+
+ @Override public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/libart/src/main/java/java/lang/reflect/ArtField.java b/libart/src/main/java/java/lang/reflect/ArtField.java
new file mode 100644
index 0000000..6fdcdb2
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/ArtField.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.android.dex.Dex;
+
+/**
+ * @hide
+ */
+public final class ArtField {
+
+ private Class<?> declaringClass;
+ /** Field access flags (modifiers) */
+ private int accessFlags;
+ /** Index into DexFile's field ids */
+ private int fieldDexIndex;
+ /** Offset of field in object or class */
+ private int offset;
+
+ /**
+ * Only created by art directly.
+ */
+ private ArtField() {}
+
+ public int getAccessFlags() {
+ return accessFlags;
+ }
+
+ int getDexFieldIndex() {
+ return fieldDexIndex;
+ }
+
+ int getOffset() {
+ return offset;
+ }
+
+ public String getName() {
+ if (fieldDexIndex == -1) {
+ // Proxy classes have 1 synthesized static field with no valid dex index
+ if (!declaringClass.isProxy()) {
+ throw new AssertionError();
+ }
+ return "throws";
+ }
+ Dex dex = declaringClass.getDex();
+ int nameIndex = dex.nameIndexFromFieldIndex(fieldDexIndex);
+ return declaringClass.getDexCacheString(dex, nameIndex);
+ }
+
+ Class<?> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ Class<?> getType() {
+ if (fieldDexIndex == -1) {
+ // The type of the synthesized field in a Proxy class is Class[][]
+ if (!declaringClass.isProxy()) {
+ throw new AssertionError();
+ }
+ return Class[][].class;
+ }
+ Dex dex = declaringClass.getDex();
+ int typeIndex = dex.typeIndexFromFieldIndex(fieldDexIndex);
+ return declaringClass.getDexCacheType(dex, typeIndex);
+ }
+}
diff --git a/libart/src/main/java/java/lang/reflect/ArtMethod.java b/libart/src/main/java/java/lang/reflect/ArtMethod.java
new file mode 100644
index 0000000..fd73331
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/ArtMethod.java
@@ -0,0 +1,218 @@
+/*
+ * 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) 2012 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 com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import libcore.reflect.AnnotationAccess;
+import libcore.util.EmptyArray;
+
+/**
+ * This class represents methods and constructors.
+ * @hide
+ */
+public final class ArtMethod {
+
+ /** Method's declaring class */
+ private Class<?> declaringClass;
+ /** Method access flags (modifiers) */
+ private int accessFlags;
+ /** DexFile index */
+ private int methodDexIndex;
+ /** Dispatch table entry */
+ private int methodIndex;
+ /** DexFile offset of CodeItem for this Method */
+ private int codeItemOffset;
+ /* ART compiler meta-data */
+ private int frameSizeInBytes;
+ private int coreSpillMask;
+ private int fpSpillMask;
+ private int mappingTable;
+ private int gcMap;
+ private int vmapTable;
+ /** ART: compiled managed code associated with this Method */
+ private int entryPointFromCompiledCode;
+ /** ART: entry point from interpreter associated with this Method */
+ private int entryPointFromInterpreter;
+ /** ART: if this is a native method, the native code that will be invoked */
+ private int nativeMethod;
+ /* ART: dex cache fast access */
+ private String[] dexCacheStrings;
+ Class<?>[] dexCacheResolvedTypes;
+ private ArtMethod[] dexCacheResolvedMethods;
+ private Object[] dexCacheInitializedStaticStorage;
+
+ /**
+ * Only created by art directly.
+ */
+ private ArtMethod() {}
+
+ Class getDeclaringClass() {
+ return declaringClass;
+ }
+
+ public int getAccessFlags() {
+ return accessFlags;
+ }
+
+ int getDexMethodIndex() {
+ return methodDexIndex;
+ }
+
+ public static String getMethodName(ArtMethod artMethod) {
+ artMethod = artMethod.findOverriddenMethodIfProxy();
+ Dex dex = artMethod.getDeclaringClass().getDex();
+ int nameIndex = dex.nameIndexFromMethodIndex(artMethod.getDexMethodIndex());
+ // Note, in the case of a Proxy the dex cache strings are equal.
+ return artMethod.getDexCacheString(dex, nameIndex);
+ }
+
+ /**
+ * Returns true if the given parameters match those of the method in the given order.
+ *
+ * @hide
+ */
+ public static boolean equalConstructorParameters(ArtMethod artMethod, Class<?>[] params) {
+ Dex dex = artMethod.getDeclaringClass().getDex();
+ short[] types = dex.parameterTypeIndicesFromMethodIndex(artMethod.getDexMethodIndex());
+ if (types.length != params.length) {
+ return false;
+ }
+ for (int i = 0; i < types.length; i++) {
+ if (artMethod.getDexCacheType(dex, types[i]) != params[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if the given parameters match those of this method in the given order.
+ *
+ * @hide
+ */
+ public static boolean equalMethodParameters(ArtMethod artMethod, Class<?>[] params) {
+ return equalConstructorParameters(artMethod.findOverriddenMethodIfProxy(), params);
+ }
+
+ Class<?>[] getParameterTypes() {
+ Dex dex = getDeclaringClass().getDex();
+ short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+ if (types.length == 0) {
+ return EmptyArray.CLASS;
+ }
+ Class<?>[] parametersArray = new Class[types.length];
+ for (int i = 0; i < types.length; i++) {
+ // Note, in the case of a Proxy the dex cache types are equal.
+ parametersArray[i] = getDexCacheType(dex, types[i]);
+ }
+ return parametersArray;
+ }
+
+ Class<?> getReturnType() {
+ Dex dex = declaringClass.getDex();
+ int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(methodDexIndex);
+ // Note, in the case of a Proxy the dex cache types are equal.
+ return getDexCacheType(dex, returnTypeIndex);
+ }
+
+ /**
+ * Performs a comparison of the parameters to this method with the given parameters.
+ *
+ * @hide
+ */
+ int compareParameters(Class<?>[] params) {
+ Dex dex = getDeclaringClass().getDex();
+ short[] types = dex.parameterTypeIndicesFromMethodIndex(methodDexIndex);
+ int length = Math.min(types.length, params.length);
+ for (int i = 0; i < length; i++) {
+ Class<?> aType = getDexCacheType(dex, types[i]);
+ Class<?> bType = params[i];
+ if (aType != bType) {
+ int comparison = aType.getName().compareTo(bType.getName());
+ if (comparison != 0) {
+ return comparison;
+ }
+ }
+ }
+ return types.length - params.length;
+ }
+
+ Annotation[][] getParameterAnnotations() {
+ return AnnotationAccess.getParameterAnnotations(declaringClass, methodDexIndex);
+ }
+
+ /**
+ * Returns a string from the dex cache, computing the string from the dex file if necessary.
+ * Note this method replicates {@link java.lang.Class#getDexCacheString(Dex, int)}, but in
+ * Method we can avoid one indirection.
+ */
+ private String getDexCacheString(Dex dex, int dexStringIndex) {
+ String s = (String) dexCacheStrings[dexStringIndex];
+ if (s == null) {
+ s = dex.strings().get(dexStringIndex).intern();
+ dexCacheStrings[dexStringIndex] = s;
+ }
+ return s;
+ }
+
+ /**
+ * Returns a resolved type from the dex cache, computing the string from the dex file if
+ * necessary. Note this method delegates to {@link java.lang.Class#getDexCacheType(Dex, int)},
+ * but in Method we can avoid one indirection.
+ */
+ private Class<?> getDexCacheType(Dex dex, int dexTypeIndex) {
+ Class<?> resolvedType = dexCacheResolvedTypes[dexTypeIndex];
+ if (resolvedType == null) {
+ resolvedType = declaringClass.getDexCacheType(dex, dexTypeIndex);
+ }
+ return resolvedType;
+ }
+
+ /**
+ * Returns the {@code ArtMethod} that this method overrides for
+ * proxy methods, otherwise returns this method. Used to determine
+ * the interface method overridden by a proxy method (as the proxy
+ * method doesn't directly support operations such as {@link
+ * Method#getName}).
+ */
+ ArtMethod findOverriddenMethodIfProxy() {
+ if (declaringClass.isProxy()) {
+ // Proxy method's declaring class' dex cache refers to that of Proxy. The local cache in
+ // Method refers to the original interface's dex cache and is ensured to be resolved by
+ // proxy generation.
+ return dexCacheResolvedMethods[methodDexIndex];
+ }
+ return this;
+ }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Constructor.java b/libart/src/main/java/java/lang/reflect/Constructor.java
new file mode 100644
index 0000000..b3df2f0
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Constructor.java
@@ -0,0 +1,327 @@
+/*
+ * 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 com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+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 AbstractMethod implements GenericDeclaration, Member {
+
+ private static final Comparator<Method> ORDER_BY_SIGNATURE = null; // Unused; must match Method.
+
+ /**
+ * @hide
+ */
+ public Constructor(ArtMethod artMethod) {
+ super(artMethod);
+ }
+
+ public Annotation[] getAnnotations() {
+ return super.getAnnotations();
+ }
+
+ /**
+ * Returns the modifiers for this constructor. The {@link Modifier} class
+ * should be used to decode the result.
+ */
+ @Override public int getModifiers() {
+ return super.getModifiers();
+ }
+
+ /**
+ * Returns true if this constructor takes a variable number of arguments.
+ */
+ public boolean isVarArgs() {
+ return super.isVarArgs();
+ }
+
+ /**
+ * Returns true if this constructor is synthetic (artificially introduced by the compiler).
+ */
+ @Override public boolean isSynthetic() {
+ return super.isSynthetic();
+ }
+
+ /**
+ * Returns the name of this constructor.
+ */
+ @Override public String getName() {
+ return getDeclaringClass().getName();
+ }
+
+ /**
+ * Returns the class that declares this constructor.
+ */
+ @Override public Class<T> getDeclaringClass() {
+ return (Class<T>) super.getDeclaringClass();
+ }
+
+ /**
+ * Returns the exception types as an array of {@code Class} instances. If
+ * this constructor has no declared exceptions, an empty array will be
+ * returned.
+ */
+ public Class<?>[] getExceptionTypes() {
+ // TODO: use dex cache to speed looking up class
+ return AnnotationAccess.getExceptions(this);
+ }
+
+ /**
+ * 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.
+ */
+ public Class<?>[] getParameterTypes() {
+ return super.getParameterTypes();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Equivalent to {@code getDeclaringClass().getName().hashCode()}.
+ */
+ @Override public int hashCode() {
+ return getDeclaringClass().getName().hashCode();
+ }
+
+ /**
+ * Returns true if {@code other} has the same declaring class and parameters
+ * as this constructor.
+ */
+ @Override public boolean equals(Object other) {
+ return super.equals(other);
+ }
+
+ @Override public TypeVariable<Constructor<T>>[] getTypeParameters() {
+ GenericInfo info = getMethodOrConstructorGenericInfo();
+ return (TypeVariable<Constructor<T>>[]) info.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() {
+ return super.toGenericString();
+ }
+
+ /**
+ * 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() {
+ return super.getGenericParameterTypes();
+ }
+
+ /**
+ * 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() {
+ return super.getGenericExceptionTypes();
+ }
+
+ @Override public Annotation[] getDeclaredAnnotations() {
+ List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+ return result.toArray(new Annotation[result.size()]);
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+ }
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return AnnotationAccess.getDeclaredAnnotation(this, 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() {
+ return artMethod.getParameterAnnotations();
+ }
+
+ /**
+ * 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")
+ String getSignature() {
+ StringBuilder result = new StringBuilder();
+
+ result.append('(');
+ Class<?>[] parameterTypes = getParameterTypes();
+ for (Class<?> parameterType : parameterTypes) {
+ result.append(Types.getSignature(parameterType));
+ }
+ result.append(")V");
+
+ return result.toString();
+ }
+
+ /**
+ * Returns a new instance of the declaring class, initialized by dynamically
+ * invoking the constructor represented by this {@code Constructor} object.
+ * This reproduces the effect of {@code new declaringClass(arg1, arg2, ... ,
+ * argN)} This method performs the following:
+ * <ul>
+ * <li>A new instance of the declaring class is created. If the declaring
+ * class cannot be instantiated (i.e. abstract class, an interface, an array
+ * type, or a primitive type) then an InstantiationException is thrown.</li>
+ * <li>If this Constructor object is enforcing access control (see
+ * {@link AccessibleObject}) and this constructor is not accessible from the
+ * current context, an IllegalAccessException is thrown.</li>
+ * <li>If the number of arguments passed and the number of parameters do not
+ * match, an IllegalArgumentException is thrown.</li>
+ * <li>For each argument passed:
+ * <ul>
+ * <li>If the corresponding parameter type is a primitive type, the argument
+ * is unboxed. If the unboxing fails, an IllegalArgumentException is
+ * thrown.</li>
+ * <li>If the resulting argument cannot be converted to the parameter type
+ * via a widening conversion, an IllegalArgumentException is thrown.</li>
+ * </ul>
+ * <li>The constructor represented by this {@code Constructor} object is
+ * then invoked. If an exception is thrown during the invocation, it is
+ * caught and wrapped in an InvocationTargetException. This exception is
+ * then thrown. If the invocation completes normally, the newly initialized
+ * object is returned.
+ * </ul>
+ *
+ * @param args
+ * the arguments to the constructor
+ *
+ * @return the new, initialized, object
+ *
+ * @exception InstantiationException
+ * if the class cannot be instantiated
+ * @exception IllegalAccessException
+ * if this constructor is not accessible
+ * @exception IllegalArgumentException
+ * if an incorrect number of arguments are passed, or an
+ * argument could not be converted by a widening conversion
+ * @exception InvocationTargetException
+ * if an exception was thrown by the invoked constructor
+ *
+ * @see AccessibleObject
+ */
+ public native T newInstance(Object... args) throws InstantiationException,
+ IllegalAccessException, IllegalArgumentException, 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(getDeclaringClass().getName());
+ result.append("(");
+ Class<?>[] parameterTypes = getParameterTypes();
+ result.append(Types.toString(parameterTypes));
+ result.append(")");
+ Class<?>[] exceptionTypes = getExceptionTypes();
+ if (exceptionTypes.length > 0) {
+ result.append(" throws ");
+ result.append(Types.toString(exceptionTypes));
+ }
+
+ return result.toString();
+ }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Field.java b/libart/src/main/java/java/lang/reflect/Field.java
new file mode 100644
index 0000000..4e982c7
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Field.java
@@ -0,0 +1,792 @@
+/*
+ * 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 com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+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) {
+ if (a == b) {
+ return 0;
+ }
+ int comparison = a.getName().compareTo(b.getName());
+ if (comparison != 0) {
+ return comparison;
+ }
+ Class<?> aType = a.getDeclaringClass();
+ Class<?> bType = b.getDeclaringClass();
+ if (aType == bType) {
+ return 0;
+ } else {
+ return aType.getName().compareTo(bType.getName());
+ }
+ }
+ };
+
+ private final ArtField artField;
+
+ /**
+ * @hide
+ */
+ public Field(ArtField artField) {
+ if (artField == null) {
+ throw new NullPointerException("artField == null");
+ }
+ this.artField = artField;
+ }
+
+ /**
+ * 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
+ */
+ @Override public int getModifiers() {
+ return artField.getAccessFlags() & 0xffff; // mask out bits not used by Java
+ }
+
+ /**
+ * 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() {
+ return (artField.getAccessFlags() & Modifier.ENUM) != 0;
+ }
+
+ /**
+ * Indicates whether or not this field is synthetic.
+ *
+ * @return {@code true} if this field is synthetic, {@code false} otherwise
+ */
+ @Override public boolean isSynthetic() {
+ return (artField.getAccessFlags() & Modifier.SYNTHETIC) != 0;
+ }
+
+ /**
+ * Returns the name of this field.
+ *
+ * @return the name of this field
+ */
+ @Override public String getName() {
+ return artField.getName();
+ }
+
+ @Override public Class<?> getDeclaringClass() {
+ return artField.getDeclaringClass();
+ }
+
+ /**
+ * Return the {@link Class} associated with the type of this field.
+ *
+ * @return the type of this field
+ */
+ public Class<?> getType() {
+ return artField.getType();
+ }
+
+ /**
+ * Returns the index of this field's ID in its dex file.
+ *
+ * @hide
+ */
+ public int getDexFieldIndex() {
+ return artField.getDexFieldIndex();
+ }
+
+ /**
+ * Returns the offset of the field within an instance, or for static fields, the class.
+ *
+ * @hide
+ */
+ public int getOffset() {
+ return artField.getOffset();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Equivalent to {@code getDeclaringClass().getName().hashCode() ^ getName().hashCode()}.
+ */
+ @Override public int hashCode() {
+ return getDeclaringClass().getName().hashCode() ^ getName().hashCode();
+ }
+
+ /**
+ * Returns true if {@code other} has the same declaring class, name and type
+ * as this field.
+ */
+ @Override public boolean equals(Object other) {
+ if (!(other instanceof Field)) {
+ return false;
+ }
+ // exactly one instance of each member in this runtime
+ return this.artField == ((Field) other).artField;
+ }
+
+ /**
+ * 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
+ Types.appendGenericType(sb, getGenericType());
+ sb.append(' ');
+ // append full field name
+ sb.append(getDeclaringClass().getName()).append('.').append(getName());
+ return sb.toString();
+ }
+
+ /**
+ * 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() {
+ String signatureAttribute = AnnotationAccess.getSignature(this);
+ Class<?> declaringClass = getDeclaringClass();
+ ClassLoader cl = declaringClass.getClassLoader();
+ GenericSignatureParser parser = new GenericSignatureParser(cl);
+ parser.parseForField(declaringClass, signatureAttribute);
+ Type genericType = parser.fieldType;
+ if (genericType == null) {
+ genericType = getType();
+ }
+ return genericType;
+ }
+
+ /**
+ * 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
+ */
+ @SuppressWarnings("unused")
+ private String getSignature() {
+ return Types.getSignature(getType());
+ }
+
+ @Override public Annotation[] getDeclaredAnnotations() {
+ List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+ return result.toArray(new Annotation[result.size()]);
+ }
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return AnnotationAccess.getDeclaredAnnotation(this, annotationType);
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+ }
+
+ /**
+ * 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 native Object get(Object object) throws IllegalAccessException, IllegalArgumentException;
+
+ /**
+ * 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 native boolean getBoolean(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native byte getByte(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native char getChar(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native double getDouble(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native float getFloat(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native int getInt(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native long getLong(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native short getShort(Object object) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void set(Object object, Object value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setBoolean(Object object, boolean value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setByte(Object object, byte value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setChar(Object object, char value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setDouble(Object object, double value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setFloat(Object object, float value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setInt(Object object, int value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setLong(Object object, long value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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 native void setShort(Object object, short value) throws IllegalAccessException,
+ IllegalArgumentException;
+
+ /**
+ * 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(' ');
+ }
+ Types.appendTypeName(result, getType());
+ result.append(' ');
+ result.append(getDeclaringClass().getName());
+ result.append('.');
+ result.append(getName());
+ return result.toString();
+ }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Method.java b/libart/src/main/java/java/lang/reflect/Method.java
new file mode 100644
index 0000000..3089932
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Method.java
@@ -0,0 +1,439 @@
+/*
+ * 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 com.android.dex.Dex;
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+import java.util.List;
+import libcore.reflect.AnnotationAccess;
+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 AbstractMethod implements GenericDeclaration, Member {
+
+ /**
+ * Orders methods by their name, parameters and return type.
+ *
+ * @hide
+ */
+ public static final Comparator<Method> ORDER_BY_SIGNATURE = new Comparator<Method>() {
+ @Override public int compare(Method a, Method b) {
+ if (a == b) {
+ return 0;
+ }
+ int comparison = a.getName().compareTo(b.getName());
+ if (comparison == 0) {
+ comparison = a.artMethod.findOverriddenMethodIfProxy().compareParameters(
+ b.getParameterTypes());
+ if (comparison == 0) {
+ // This is necessary for methods that have covariant return types.
+ Class<?> aReturnType = a.getReturnType();
+ Class<?> bReturnType = b.getReturnType();
+ if (aReturnType == bReturnType) {
+ comparison = 0;
+ } else {
+ comparison = aReturnType.getName().compareTo(bReturnType.getName());
+ }
+ }
+ }
+ return comparison;
+ }
+ };
+
+ /**
+ * @hide
+ */
+ public Method(ArtMethod artMethod) {
+ super(artMethod);
+ }
+
+ ArtMethod getArtMethod() {
+ return artMethod;
+ }
+
+ public Annotation[] getAnnotations() {
+ return super.getAnnotations();
+ }
+
+ /**
+ * 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
+ */
+ @Override public int getModifiers() {
+ return super.getModifiers();
+ }
+
+ /**
+ * 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() {
+ return super.isVarArgs();
+ }
+
+ /**
+ * Indicates whether or not this method is a bridge.
+ *
+ * @return {@code true} if this method is a bridge, {@code false} otherwise
+ */
+ public boolean isBridge() {
+ return super.isBridge();
+
+ }
+
+ /**
+ * Indicates whether or not this method is synthetic.
+ *
+ * @return {@code true} if this method is synthetic, {@code false} otherwise
+ */
+ @Override public boolean isSynthetic() {
+ return super.isSynthetic();
+ }
+
+ /**
+ * Returns the name of the method represented by this {@code Method}
+ * instance.
+ *
+ * @return the name of this method
+ */
+ @Override public String getName() {
+ return ArtMethod.getMethodName(artMethod);
+ }
+
+ /**
+ * Returns the class that declares this method.
+ */
+ @Override public Class<?> getDeclaringClass() {
+ return super.getDeclaringClass();
+ }
+
+ /**
+ * 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 (getDeclaringClass().isProxy()) {
+ return getExceptionTypesNative();
+ } else {
+ // TODO: use dex cache to speed looking up class
+ return AnnotationAccess.getExceptions(this);
+ }
+ }
+
+ private native Class<?>[] getExceptionTypesNative();
+
+ /**
+ * 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
+ */
+ @Override public Class<?>[] getParameterTypes() {
+ return artMethod.findOverriddenMethodIfProxy().getParameterTypes();
+ }
+
+ /**
+ * Returns the {@code Class} associated with the return type of this
+ * method.
+ *
+ * @return the return type
+ */
+ public Class<?> getReturnType() {
+ return artMethod.findOverriddenMethodIfProxy().getReturnType();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Equivalent to {@code getDeclaringClass().getName().hashCode() ^ getName().hashCode()}.
+ */
+ @Override public int hashCode() {
+ return getDeclaringClass().getName().hashCode() ^ getName().hashCode();
+ }
+
+ /**
+ * Returns true if {@code other} has the same declaring class, name,
+ * parameters and return type as this method.
+ */
+ @Override public boolean equals(Object other) {
+ return super.equals(other);
+ }
+
+ /**
+ * Returns true if this and {@code method} have the same name and the same
+ * parameters in the same order. Such methods can share implementation if
+ * one method's return types is assignable to the other.
+ *
+ * @hide needed by Proxy
+ */
+ boolean equalNameAndParameters(Method m) {
+ return getName().equals(m.getName()) &&
+ ArtMethod.equalMethodParameters(artMethod,m.getParameterTypes());
+ }
+
+ /**
+ * Returns the string representation of the method's declaration, including
+ * the type parameters.
+ *
+ * @return the string representation of this method
+ */
+ public String toGenericString() {
+ return super.toGenericString();
+ }
+
+ @Override public TypeVariable<Method>[] getTypeParameters() {
+ GenericInfo info = getMethodOrConstructorGenericInfo();
+ return (TypeVariable<Method>[]) info.formalTypeParameters.clone();
+ }
+
+ /**
+ * 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() {
+ return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericParameterTypes, false);
+ }
+
+ @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType);
+ }
+
+ /**
+ * 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() {
+ return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericExceptionTypes, false);
+ }
+
+ /**
+ * 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() {
+ return Types.getType(getMethodOrConstructorGenericInfo().genericReturnType);
+ }
+
+ @Override public Annotation[] getDeclaredAnnotations() {
+ List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this);
+ return result.toArray(new Annotation[result.size()]);
+ }
+
+ @Override public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ if (annotationType == null) {
+ throw new NullPointerException("annotationType == null");
+ }
+ return AnnotationAccess.getDeclaredAnnotation(this, annotationType);
+ }
+
+ /**
+ * 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() {
+ return artMethod.findOverriddenMethodIfProxy().getParameterAnnotations();
+ }
+
+ /**
+ * 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 AnnotationAccess.getDefaultValue(this);
+ }
+
+ /**
+ * 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 native Object invoke(Object receiver, Object... args)
+ 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(getReturnType().getName());
+ result.append(' ');
+ result.append(getDeclaringClass().getName());
+ result.append('.');
+ result.append(getName());
+ result.append("(");
+ Class<?>[] parameterTypes = getParameterTypes();
+ result.append(Types.toString(parameterTypes));
+ result.append(")");
+ Class<?>[] exceptionTypes = getExceptionTypes();
+ if (exceptionTypes.length != 0) {
+ result.append(" throws ");
+ result.append(Types.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")
+ String getSignature() {
+ StringBuilder result = new StringBuilder();
+
+ result.append('(');
+ Class<?>[] parameterTypes = getParameterTypes();
+ for (Class<?> parameterType : parameterTypes) {
+ result.append(Types.getSignature(parameterType));
+ }
+ result.append(')');
+ result.append(Types.getSignature(getReturnType()));
+
+ return result.toString();
+ }
+}
diff --git a/libart/src/main/java/java/lang/reflect/Proxy.java b/libart/src/main/java/java/lang/reflect/Proxy.java
new file mode 100644
index 0000000..51b3ad5
--- /dev/null
+++ b/libart/src/main/java/java/lang/reflect/Proxy.java
@@ -0,0 +1,381 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import libcore.util.EmptyArray;
+
+/**
+ * {@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;
+
+ private static int NextClassNameIndex = 0;
+
+ /**
+ * Orders methods by their name, parameters, return type and inheritance relationship.
+ *
+ * @hide
+ */
+ private static final Comparator<Method> ORDER_BY_SIGNATURE_AND_SUBTYPE = new Comparator<Method>() {
+ @Override public int compare(Method a, Method b) {
+ int comparison = Method.ORDER_BY_SIGNATURE.compare(a, b);
+ if (comparison != 0) {
+ return comparison;
+ }
+ Class<?> aClass = a.getDeclaringClass();
+ Class<?> bClass = b.getDeclaringClass();
+ if (aClass == bClass) {
+ return 0;
+ } else if (aClass.isAssignableFrom(bClass)) {
+ return 1;
+ } else if (bClass.isAssignableFrom(aClass)) {
+ return -1;
+ } else {
+ return 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 {
+ if (loader == null) {
+ loader = ClassLoader.getSystemClassLoader();
+ }
+
+ if (interfaces == null) {
+ throw new NullPointerException("interfaces == null");
+ }
+
+ Set<Class<?>> interfacesSet = new CopyOnWriteArraySet<Class<?>>(Arrays.asList(interfaces));
+
+ Class<?> proxy = loader.proxyCache.get(interfacesSet);
+ if (proxy != null) {
+ return proxy;
+ }
+
+ if (interfacesSet.size() != interfaces.length) {
+ throw new IllegalArgumentException(
+ "interfaces has duplicates: " + Arrays.toString(interfaces));
+ }
+ String commonPackageName = null;
+ for (Class<?> c : interfaces) {
+ if (c == null) {
+ throw new NullPointerException("interfaces contained a null element");
+ }
+ if (!c.isInterface()) {
+ throw new IllegalArgumentException(c + " is not an interface");
+ }
+ if (!isVisibleToClassLoader(loader, c)) {
+ throw new IllegalArgumentException(c + " is not visible from class loader");
+ }
+ if (!Modifier.isPublic(c.getModifiers())) {
+ String packageName = c.getPackageName$();
+ if (packageName == null) {
+ packageName = "";
+ }
+ if (commonPackageName != null && !commonPackageName.equals(packageName)) {
+ throw new IllegalArgumentException(
+ "non-public interfaces must be in the same package");
+ }
+ commonPackageName = packageName;
+ }
+ }
+
+ String baseName = commonPackageName != null && !commonPackageName.isEmpty()
+ ? commonPackageName + ".$Proxy"
+ : "$Proxy";
+ String name = baseName + NextClassNameIndex++;
+
+ List<Method> methods = getMethods(interfaces);
+ Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
+ validateReturnTypes(methods);
+ List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
+
+ ArtMethod[] methodsArray = new ArtMethod[methods.size()];
+ for (int i = 0; i < methodsArray.length; i++) {
+ methodsArray[i] = methods.get(i).getArtMethod();
+ }
+ Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
+ Class<?> result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
+ loader.proxyCache.put(interfacesSet, result);
+ return result;
+ }
+
+ private static boolean isVisibleToClassLoader(ClassLoader loader, Class<?> c) {
+ try {
+ return loader == c.getClassLoader() || c == Class.forName(c.getName(), false, loader);
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * 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 invocationHandler
+ * 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 invocationHandler)
+ throws IllegalArgumentException {
+
+ if (invocationHandler == null) {
+ throw new NullPointerException("invocationHandler == null");
+ }
+ Exception cause;
+ try {
+ return getProxyClass(loader, interfaces)
+ .getConstructor(InvocationHandler.class)
+ .newInstance(invocationHandler);
+ } catch (NoSuchMethodException e) {
+ cause = e;
+ } catch (IllegalAccessException e) {
+ cause = e;
+ } catch (InstantiationException e) {
+ cause = e;
+ } catch (InvocationTargetException e) {
+ cause = e;
+ }
+ AssertionError error = new AssertionError();
+ error.initCause(cause);
+ throw error;
+ }
+
+ /**
+ * 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) {
+ return cl.isProxy();
+ }
+
+ /**
+ * 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 {
+ // TODO: return false for subclasses of Proxy not created by generateProxy()
+ if (!(proxy instanceof Proxy)) {
+ throw new IllegalArgumentException("not a proxy instance");
+ }
+ return ((Proxy) proxy).h;
+ }
+
+ private static List<Method> getMethods(Class<?>[] interfaces) {
+ List<Method> result = new ArrayList<Method>();
+ try {
+ result.add(Object.class.getMethod("equals", Object.class));
+ result.add(Object.class.getMethod("hashCode", EmptyArray.CLASS));
+ result.add(Object.class.getMethod("toString", EmptyArray.CLASS));
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError();
+ }
+
+ getMethodsRecursive(interfaces, result);
+ return result;
+ }
+
+ /**
+ * Fills {@code proxiedMethods} with the methods of {@code interfaces} and
+ * the interfaces they extend. May contain duplicates.
+ */
+ private static void getMethodsRecursive(Class<?>[] interfaces, List<Method> methods) {
+ for (Class<?> i : interfaces) {
+ getMethodsRecursive(i.getInterfaces(), methods);
+ Collections.addAll(methods, i.getDeclaredMethods());
+ }
+ }
+
+ /**
+ * Throws if any two methods in {@code methods} have the same name and
+ * parameters but incompatible return types.
+ *
+ * @param methods the methods to find exceptions for, ordered by name and
+ * signature.
+ */
+ private static void validateReturnTypes(List<Method> methods) {
+ Method vs = null;
+ for (Method method : methods) {
+ if (vs == null || !vs.equalNameAndParameters(method)) {
+ vs = method; // this has a different name or parameters
+ continue;
+ }
+ Class<?> returnType = method.getReturnType();
+ Class<?> vsReturnType = vs.getReturnType();
+ if (returnType.isInterface() && vsReturnType.isInterface()) {
+ // all interfaces are mutually compatible
+ } else if (vsReturnType.isAssignableFrom(returnType)) {
+ vs = method; // the new return type is a subtype; use it instead
+ } else if (!returnType.isAssignableFrom(vsReturnType)) {
+ throw new IllegalArgumentException("proxied interface methods have incompatible "
+ + "return types:\n " + vs + "\n " + method);
+ }
+ }
+ }
+
+ /**
+ * Remove methods that have the same name, parameters and return type. This
+ * computes the exceptions of each method; this is the intersection of the
+ * exceptions of equivalent methods.
+ *
+ * @param methods the methods to find exceptions for, ordered by name and
+ * signature.
+ */
+ private static List<Class<?>[]> deduplicateAndGetExceptions(List<Method> methods) {
+ List<Class<?>[]> exceptions = new ArrayList<Class<?>[]>(methods.size());
+
+ for (int i = 0; i < methods.size(); ) {
+ Method method = methods.get(i);
+ Class<?>[] exceptionTypes = method.getExceptionTypes();
+
+ if (i > 0 && Method.ORDER_BY_SIGNATURE.compare(method, methods.get(i - 1)) == 0) {
+ exceptions.set(i - 1, intersectExceptions(exceptions.get(i - 1), exceptionTypes));
+ methods.remove(i);
+ } else {
+ exceptions.add(exceptionTypes);
+ i++;
+ }
+ }
+ return exceptions;
+ }
+
+ /**
+ * Returns the exceptions that are declared in both {@code aExceptions} and
+ * {@code bExceptions}. If an exception type in one array is a subtype of an
+ * exception from the other, the subtype is included in the intersection.
+ */
+ private static Class<?>[] intersectExceptions(Class<?>[] aExceptions, Class<?>[] bExceptions) {
+ if (aExceptions.length == 0 || bExceptions.length == 0) {
+ return EmptyArray.CLASS;
+ }
+ if (Arrays.equals(aExceptions, bExceptions)) {
+ return aExceptions;
+ }
+ Set<Class<?>> intersection = new HashSet<Class<?>>();
+ for (Class<?> a : aExceptions) {
+ for (Class<?> b : bExceptions) {
+ if (a.isAssignableFrom(b)) {
+ intersection.add(b);
+ } else if (b.isAssignableFrom(a)) {
+ intersection.add(a);
+ }
+ }
+ }
+ return intersection.toArray(new Class<?>[intersection.size()]);
+ }
+
+ private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
+ ClassLoader loader, ArtMethod[] methods,
+ Class<?>[][] exceptions);
+
+ /*
+ * The VM clones this method's descriptor when generating a proxy class.
+ * There is no implementation.
+ */
+ private static native void constructorPrototype(InvocationHandler h);
+
+ static Object invoke(Proxy proxy, ArtMethod method, Object[] args) throws Throwable {
+ InvocationHandler h = proxy.h;
+ return h.invoke(proxy, new Method(method), args);
+ }
+}
diff --git a/libart/src/main/java/sun/misc/Unsafe.java b/libart/src/main/java/sun/misc/Unsafe.java
new file mode 100644
index 0000000..0a4d8b2
--- /dev/null
+++ b/libart/src/main/java/sun/misc/Unsafe.java
@@ -0,0 +1,338 @@
+/*
+ * 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 field.getOffset();
+ }
+
+ /**
+ * 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) {
+ Class<?> component = clazz.getComponentType();
+ if (component == null) {
+ throw new IllegalArgumentException("Valid for array classes only: " + clazz);
+ }
+ // TODO: make the following not specific to the object model.
+ int offset = 12;
+ if (component == long.class || component == double.class) {
+ offset += 4; // 4 bytes of padding.
+ }
+ return offset;
+ }
+
+ /**
+ * 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) {
+ Class<?> component = clazz.getComponentType();
+ if (component == null) {
+ throw new IllegalArgumentException("Valid for array classes only: " + clazz);
+ }
+ // TODO: make the following not specific to the object model.
+ if (!component.isPrimitive()) {
+ return 4;
+ } else if (component == long.class || component == double.class) {
+ return 8;
+ } else if (component == int.class || component == float.class) {
+ return 4;
+ } else if (component == char.class || component == short.class) {
+ return 2;
+ } else {
+ // component == byte.class || component == boolean.class.
+ return 1;
+ }
+ }
+
+ /**
+ * 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/libdvm/src/main/java/dalvik/system/VMRuntime.java b/libdvm/src/main/java/dalvik/system/VMRuntime.java
index 48685f6..f26ca54 100644
--- a/libdvm/src/main/java/dalvik/system/VMRuntime.java
+++ b/libdvm/src/main/java/dalvik/system/VMRuntime.java
@@ -70,6 +70,11 @@
public native String vmVersion();
/**
+ * Returns the name of the shared library providing the VM implementation.
+ */
+ public native String vmLibrary();
+
+ /**
* 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
@@ -133,7 +138,7 @@
/**
* This method exists for binary compatibility. It was part of a
- * heap sizing API which was removed in Honeycomb.
+ * heap sizing API which was removed in Android 3.0 (Honeycomb).
*/
@Deprecated
public long getMinimumHeapSize() {
@@ -142,7 +147,7 @@
/**
* This method exists for binary compatibility. It was part of a
- * heap sizing API which was removed in Honeycomb.
+ * heap sizing API which was removed in Android 3.0 (Honeycomb).
*/
@Deprecated
public long setMinimumHeapSize(long size) {
@@ -175,7 +180,7 @@
/**
* This method exists for binary compatibility. It was part of
- * the external allocation API which was removed in Honeycomb.
+ * the external allocation API which was removed in Android 3.0 (Honeycomb).
*/
@Deprecated
public boolean trackExternalAllocation(long size) {
@@ -184,14 +189,14 @@
/**
* This method exists for binary compatibility. It was part of
- * the external allocation API which was removed in Honeycomb.
+ * 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 Honeycomb.
+ * the external allocation API which was removed in Android 3.0 (Honeycomb).
*/
@Deprecated
public long getExternalBytesAllocated() {
@@ -233,4 +238,24 @@
* 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);
+
+ /**
+ * Fill in dex caches with classes, fields, and methods that are already loaded.
+ */
+ public native void preloadDexCaches();
}
diff --git a/libdvm/src/main/java/java/lang/Class.java b/libdvm/src/main/java/java/lang/Class.java
index c8064cb..4b36f3c 100644
--- a/libdvm/src/main/java/java/lang/Class.java
+++ b/libdvm/src/main/java/java/lang/Class.java
@@ -54,14 +54,15 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import libcore.util.CollectionUtils;
-import libcore.util.EmptyArray;
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
@@ -122,12 +123,24 @@
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 transient int dexTypeIndex;
-
private Class() {
// Prevent this class to be instantiated, instance
// should be created by JVM only
@@ -139,44 +152,48 @@
*/
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 type index of this class in its own Dex, or 0 if it is unknown. If a
- * class is referenced by multiple Dex files, it will have a different type
- * index in each. Dex files support 65534 type indices, with 65535
- * representing no index.
- *
- * TODO: 0 is a valid index; this should be -1 if it is unknown
+ * The class def of this class in its own Dex, or -1 if there is no class def.
*
* @hide
*/
- public int getTypeIndex() {
- int result = dexTypeIndex;
- if (result == 0) { // uncomputed => Dalvik
- result = AnnotationAccess.computeTypeIndex(getDex(), this);
- dexTypeIndex = result;
+ public int getDexClassDefIndex() {
+ if (!dexIndicesInitialized) {
+ computeDexIndices();
}
- return result;
+ return dexClassDefIndex;
}
/**
- * Get the Signature attribute for this class. Returns null if not found.
+ * 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
*/
- private String getSignatureAttribute() {
- Object[] annotation = getSignatureAnnotation();
-
- if (annotation == null) {
- return null;
+ public int getDexTypeIndex() {
+ if (!dexIndicesInitialized) {
+ computeDexIndices();
}
-
- return StringUtils.combineStrings(annotation);
+ return dexTypeIndex;
}
/**
- * Get the Signature annotation for this class. Returns null if not found.
- */
- native private Object[] getSignatureAnnotation();
-
- /**
* Returns a {@code Class} object which represents the class with the
* given name. The name should be the name of a non-primitive class, as described in
* the {@link Class class definition}.
@@ -784,13 +801,18 @@
synchronized (Caches.genericInterfaces) {
result = Caches.genericInterfaces.get(this);
if (result == null) {
- GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
- parser.parseForClass(this, getSignatureAttribute());
- result = Types.getClonedTypeArray(parser.interfaceTypes);
+ 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;
+ }
+ return (result.length == 0) ? result : result.clone();
}
/**
@@ -798,9 +820,14 @@
* class}.
*/
public Type getGenericSuperclass() {
- GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
- parser.parseForClass(this, getSignatureAttribute());
- return Types.getType(parser.superclassType);
+ Type genericSuperclass = getSuperclass();
+ String annotationSignature = AnnotationAccess.getSignature(this);
+ if (annotationSignature != null) {
+ GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
+ parser.parseForClass(this, annotationSignature);
+ genericSuperclass = parser.superclassType;
+ }
+ return Types.getType(genericSuperclass);
}
/**
@@ -1040,9 +1067,13 @@
*/
@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, getSignatureAttribute());
- return parser.formalTypeParameters.clone();
+ parser.parseForClass(this, annotationSignature);
+ return parser.formalTypeParameters;
}
/**
@@ -1104,7 +1135,11 @@
* {@code enum}.
*/
public boolean isEnum() {
- return ((getModifiers() & 0x4000) != 0) && (getSuperclass() == Enum.class);
+ if (getSuperclass() != Enum.class) {
+ return false;
+ }
+ int mod = getModifiers(this, true);
+ return (mod & 0x4000) != 0;
}
/**
@@ -1179,9 +1214,8 @@
public String toString() {
if (isPrimitive()) {
return getSimpleName();
- } else {
- return (isInterface() ? "interface " : "class ") + getName();
}
+ return (isInterface() ? "interface " : "class ") + getName();
}
/**
@@ -1267,13 +1301,48 @@
*
* @hide
*/
- public int getAnnotationDirectoryOffset() {
- return AnnotationAccess.typeIndexToAnnotationDirectoryOffset(getDex(), getTypeIndex());
+ 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[]>(50);
+ = new BasicLruCache<Class, Type[]>(8);
}
-
}
diff --git a/libdvm/src/main/java/java/lang/reflect/Constructor.java b/libdvm/src/main/java/java/lang/reflect/Constructor.java
index a1a069f..acce44f 100644
--- a/libdvm/src/main/java/java/lang/reflect/Constructor.java
+++ b/libdvm/src/main/java/java/lang/reflect/Constructor.java
@@ -153,12 +153,10 @@
appendTypeName(sb, getDeclaringClass());
// append parameters
sb.append('(');
- appendArrayGenericType(sb,
- Types.getClonedTypeArray(genericParameterTypes));
+ appendArrayGenericType(sb, Types.getTypeArray(genericParameterTypes, false));
sb.append(')');
// append exceptions if any
- Type[] genericExceptionTypeArray =
- Types.getClonedTypeArray(genericExceptionTypes);
+ Type[] genericExceptionTypeArray = Types.getTypeArray(genericExceptionTypes, false);
if (genericExceptionTypeArray.length > 0) {
sb.append(" throws ");
appendArrayGenericType(sb, genericExceptionTypeArray);
@@ -183,7 +181,7 @@
*/
public Type[] getGenericParameterTypes() {
initGenericTypes();
- return Types.getClonedTypeArray(genericParameterTypes);
+ return Types.getTypeArray(genericParameterTypes, true);
}
/**
@@ -203,7 +201,7 @@
*/
public Type[] getGenericExceptionTypes() {
initGenericTypes();
- return Types.getClonedTypeArray(genericExceptionTypes);
+ return Types.getTypeArray(genericExceptionTypes, true);
}
@Override
diff --git a/libdvm/src/main/java/java/lang/reflect/Method.java b/libdvm/src/main/java/java/lang/reflect/Method.java
index 028b9ca..f8efbf4 100644
--- a/libdvm/src/main/java/java/lang/reflect/Method.java
+++ b/libdvm/src/main/java/java/lang/reflect/Method.java
@@ -185,12 +185,10 @@
sb.append(".").append(getName());
// append parameters
sb.append('(');
- appendArrayGenericType(sb,
- Types.getClonedTypeArray(genericParameterTypes));
+ appendArrayGenericType(sb, Types.getTypeArray(genericParameterTypes, false));
sb.append(')');
// append exceptions if any
- Type[] genericExceptionTypeArray = Types.getClonedTypeArray(
- genericExceptionTypes);
+ Type[] genericExceptionTypeArray = Types.getTypeArray(genericExceptionTypes, false);
if (genericExceptionTypeArray.length > 0) {
sb.append(" throws ");
appendArrayGenericType(sb, genericExceptionTypeArray);
@@ -215,7 +213,7 @@
*/
public Type[] getGenericParameterTypes() {
initGenericTypes();
- return Types.getClonedTypeArray(genericParameterTypes);
+ return Types.getTypeArray(genericParameterTypes, true);
}
/**
@@ -234,7 +232,7 @@
*/
public Type[] getGenericExceptionTypes() {
initGenericTypes();
- return Types.getClonedTypeArray(genericExceptionTypes);
+ return Types.getTypeArray(genericExceptionTypes, true);
}
/**
diff --git a/luni/src/main/files/cacerts/3c9a4d3b.0 b/luni/src/main/files/cacerts/3c9a4d3b.0
new file mode 100644
index 0000000..c6e312c
--- /dev/null
+++ b/luni/src/main/files/cacerts/3c9a4d3b.0
@@ -0,0 +1,152 @@
+-----BEGIN CERTIFICATE-----
+MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
+AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
+CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
+BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND
+VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb
+qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY
+HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo
+G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA
+lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr
+IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/
+0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH
+k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47
+4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO
+m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa
+cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl
+uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI
+KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls
+ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG
+AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
+VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT
+VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG
+CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA
+cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA
+QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA
+7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA
+cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA
+QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA
+czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu
+aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt
+aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud
+DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF
+BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp
+D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU
+JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m
+AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD
+vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms
+tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH
+7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
+I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA
+h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF
+d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H
+pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 6828503384748696800 (0x5ec3b7a6437fa4e0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=ACCVRAIZ1, OU=PKIACCV, O=ACCV, C=ES
+ Validity
+ Not Before: May 5 09:37:37 2011 GMT
+ Not After : Dec 31 09:37:37 2030 GMT
+ Subject: CN=ACCVRAIZ1, OU=PKIACCV, O=ACCV, C=ES
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:9b:a9:ab:bf:61:4a:97:af:2f:97:66:9a:74:5f:
+ d0:d9:96:fd:cf:e2:e4:66:ef:1f:1f:47:33:c2:44:
+ a3:df:9a:de:1f:b5:54:dd:15:7c:69:35:11:6f:bb:
+ c8:0c:8e:6a:18:1e:d8:8f:d9:16:bc:10:48:36:5c:
+ f0:63:b3:90:5a:5c:24:37:d7:a3:d6:cb:09:71:b9:
+ f1:01:72:84:b0:7d:db:4d:80:cd:fc:d3:6f:c9:f8:
+ da:b6:0e:82:d2:45:85:a8:1b:68:a8:3d:e8:f4:44:
+ 6c:bd:a1:c2:cb:03:be:8c:3e:13:00:84:df:4a:48:
+ c0:e3:22:0a:e8:e9:37:a7:18:4c:b1:09:0d:23:56:
+ 7f:04:4d:d9:17:84:18:a5:c8:da:40:94:73:eb:ce:
+ 0e:57:3c:03:81:3a:9d:0a:a1:57:43:69:ac:57:6d:
+ 79:90:78:e5:b5:b4:3b:d8:bc:4c:8d:28:a1:a7:a3:
+ a7:ba:02:4e:25:d1:2a:ae:ed:ae:03:22:b8:6b:20:
+ 0f:30:28:54:95:7f:e0:ee:ce:0a:66:9d:d1:40:2d:
+ 6e:22:af:9d:1a:c1:05:19:d2:6f:c0:f2:9f:f8:7b:
+ b3:02:42:fb:50:a9:1d:2d:93:0f:23:ab:c6:c1:0f:
+ 92:ff:d0:a2:15:f5:53:09:71:1c:ff:45:13:84:e6:
+ 26:5e:f8:e0:88:1c:0a:fc:16:b6:a8:73:06:b8:f0:
+ 63:84:02:a0:c6:5a:ec:e7:74:df:70:ae:a3:83:25:
+ ea:d6:c7:97:87:93:a7:c6:8a:8a:33:97:60:37:10:
+ 3e:97:3e:6e:29:15:d6:a1:0f:d1:88:2c:12:9f:6f:
+ aa:a4:c6:42:eb:41:a2:e3:95:43:d3:01:85:6d:8e:
+ bb:3b:f3:23:36:c7:fe:3b:e0:a1:25:07:48:ab:c9:
+ 89:74:ff:08:8f:80:bf:c0:96:65:f3:ee:ec:4b:68:
+ bd:9d:88:c3:31:b3:40:f1:e8:cf:f6:38:bb:9c:e4:
+ d1:7f:d4:e5:58:9b:7c:fa:d4:f3:0e:9b:75:91:e4:
+ ba:52:2e:19:7e:d1:f5:cd:5a:19:fc:ba:06:f6:fb:
+ 52:a8:4b:99:04:dd:f8:f9:b4:8b:50:a3:4e:62:89:
+ f0:87:24:fa:83:42:c1:87:fa:d5:2d:29:2a:5a:71:
+ 7a:64:6a:d7:27:60:63:0d:db:ce:49:f5:8d:1f:90:
+ 89:32:17:f8:73:43:b8:d2:5a:93:86:61:d6:e1:75:
+ 0a:ea:79:66:76:88:4f:71:eb:04:25:d6:0a:5a:7a:
+ 93:e5:b9:4b:17:40:0f:b1:b6:b9:f5:de:4f:dc:e0:
+ b3:ac:3b:11:70:60:84:4a:43:6e:99:20:c0:29:71:
+ 0a:c0:65
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ CA Issuers - URI:http://www.accv.es/fileadmin/Archivos/certificados/raizaccv1.crt
+ OCSP - URI:http://ocsp.accv.es
+
+ X509v3 Subject Key Identifier:
+ D2:87:B4:E3:DF:37:27:93:55:F6:56:EA:81:E5:36:CC:8C:1E:3F:BD
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Authority Key Identifier:
+ keyid:D2:87:B4:E3:DF:37:27:93:55:F6:56:EA:81:E5:36:CC:8C:1E:3F:BD
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ User Notice:
+ Explicit Text:
+ CPS: http://www.accv.es/legislacion_c.htm
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.accv.es/fileadmin/Archivos/certificados/raizaccv1_der.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ email:accv@accv.es
+ Signature Algorithm: sha1WithRSAEncryption
+ 97:31:02:9f:e7:fd:43:67:48:44:14:e4:29:87:ed:4c:28:66:
+ d0:8f:35:da:4d:61:b7:4a:97:4d:b5:db:90:e0:05:2e:0e:c6:
+ 79:d0:f2:97:69:0f:bd:04:47:d9:be:db:b5:29:da:9b:d9:ae:
+ a9:99:d5:d3:3c:30:93:f5:8d:a1:a8:fc:06:8d:44:f4:ca:16:
+ 95:7c:33:dc:62:8b:a8:37:f8:27:d8:09:2d:1b:ef:c8:14:27:
+ 20:a9:64:44:ff:2e:d6:75:aa:6c:4d:60:40:19:49:43:54:63:
+ da:e2:cc:ba:66:e5:4f:44:7a:5b:d9:6a:81:2b:40:d5:7f:f9:
+ 01:27:58:2c:c8:ed:48:91:7c:3f:a6:00:cf:c4:29:73:11:36:
+ de:86:19:3e:9d:ee:19:8a:1b:d5:b0:ed:8e:3d:9c:2a:c0:0d:
+ d8:3d:66:e3:3c:0d:bd:d5:94:5c:e2:e2:a7:35:1b:04:00:f6:
+ 3f:5a:8d:ea:43:bd:5f:89:1d:a9:c1:b0:cc:99:e2:4d:00:0a:
+ da:c9:27:5b:e7:13:90:5c:e4:f5:33:a2:55:6d:dc:e0:09:4d:
+ 2f:b1:26:5b:27:75:00:09:c4:62:77:29:08:5f:9e:59:ac:b6:
+ 7e:ad:9f:54:30:22:03:c1:1e:71:64:fe:f9:38:0a:96:18:dd:
+ 02:14:ac:23:cb:06:1c:1e:a4:7d:8d:0d:de:27:41:e8:ad:da:
+ 15:b7:b0:23:dd:2b:a8:d3:da:25:87:ed:e8:55:44:4d:88:f4:
+ 36:7e:84:9a:78:ac:f7:0e:56:49:0e:d6:33:25:d6:84:50:42:
+ 6c:20:12:1d:2a:d5:be:bc:f2:70:81:a4:70:60:be:05:b5:9b:
+ 9e:04:44:be:61:23:ac:e9:a5:24:8c:11:80:94:5a:a2:a2:b9:
+ 49:d2:c1:dc:d1:a7:ed:31:11:2c:9e:19:a6:ee:e1:55:e1:c0:
+ ea:cf:0d:84:e4:17:b7:a2:7c:a5:de:55:25:06:ee:cc:c0:87:
+ 5c:40:da:cc:95:3f:55:e0:35:c7:b8:84:be:b4:5d:cd:7a:83:
+ 01:72:ee:87:e6:5f:1d:ae:b5:85:c6:26:df:e6:c1:9a:e9:1e:
+ 02:47:9f:2a:a8:6d:a9:5b:cf:ec:45:77:7f:98:27:9a:32:5d:
+ 2a:e3:84:ee:c5:98:66:2f:96:20:1d:dd:d8:c3:27:d7:b0:f9:
+ fe:d9:7d:cd:d0:9f:8f:0b:14:58:51:9f:2f:8b:c3:38:2d:de:
+ e8:8f:d6:8d:87:a4:f5:56:43:16:99:2c:f4:a4:56:b4:34:b8:
+ 61:37:c9:c2:58:80:1b:a0:97:a1:fc:59:8d:e9:11:f6:d1:0f:
+ 4b:55:34:46:2a:8b:86:3b
+SHA1 Fingerprint=93:05:7A:88:15:C6:4F:CE:88:2F:FA:91:16:52:28:78:BC:53:64:17
diff --git a/luni/src/main/files/cacerts/40dc992e.0 b/luni/src/main/files/cacerts/40dc992e.0
new file mode 100644
index 0000000..847cec6
--- /dev/null
+++ b/luni/src/main/files/cacerts/40dc992e.0
@@ -0,0 +1,93 @@
+-----BEGIN CERTIFICATE-----
+MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
+RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
+dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
+YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
+NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
+EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
+cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
+c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
+dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
+fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
+bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
+75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
+FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
+HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
+5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
+b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
+A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
+6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
+TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
+dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
+Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
+l7WdmplNsDz4SgCbZN2fOUvRJ9e4
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GR, O=Hellenic Academic and Research Institutions Cert. Authority, CN=Hellenic Academic and Research Institutions RootCA 2011
+ Validity
+ Not Before: Dec 6 13:49:52 2011 GMT
+ Not After : Dec 1 13:49:52 2031 GMT
+ Subject: C=GR, O=Hellenic Academic and Research Institutions Cert. Authority, CN=Hellenic Academic and Research Institutions RootCA 2011
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a9:53:00:e3:2e:a6:f6:8e:fa:60:d8:2d:95:3e:
+ f8:2c:2a:54:4e:cd:b9:84:61:94:58:4f:8f:3d:8b:
+ e4:43:f3:75:89:8d:51:e4:c3:37:d2:8a:88:4d:79:
+ 1e:b7:12:dd:43:78:4a:8a:92:e6:d7:48:d5:0f:a4:
+ 3a:29:44:35:b8:07:f6:68:1d:55:cd:38:51:f0:8c:
+ 24:31:85:af:83:c9:7d:e9:77:af:ed:1a:7b:9d:17:
+ f9:b3:9d:38:50:0f:a6:5a:79:91:80:af:37:ae:a6:
+ d3:31:fb:b5:26:09:9d:3c:5a:ef:51:c5:2b:df:96:
+ 5d:eb:32:1e:02:da:70:49:ec:6e:0c:c8:9a:37:8d:
+ f7:f1:36:60:4b:26:2c:82:9e:d0:78:f3:0d:0f:63:
+ a4:51:30:e1:f9:2b:27:12:07:d8:ea:bd:18:62:98:
+ b0:59:37:7d:be:ee:f3:20:51:42:5a:83:ef:93:ba:
+ 69:15:f1:62:9d:9f:99:39:82:a1:b7:74:2e:8b:d4:
+ c5:0b:7b:2f:f0:c8:0a:da:3d:79:0a:9a:93:1c:a5:
+ 28:72:73:91:43:9a:a7:d1:4d:85:84:b9:a9:74:8f:
+ 14:40:c7:dc:de:ac:41:64:6c:b4:19:9b:02:63:6d:
+ 24:64:8f:44:b2:25:ea:ce:5d:74:0c:63:32:5c:8d:
+ 87:e5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage:
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ A6:91:42:FD:13:61:4A:23:9E:08:A4:29:E5:D8:13:04:23:EE:41:25
+ X509v3 Name Constraints:
+ Permitted:
+ DNS:.gr
+ DNS:.eu
+ DNS:.edu
+ DNS:.org
+ email:.gr
+ email:.eu
+ email:.edu
+ email:.org
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 1f:ef:79:41:e1:7b:6e:3f:b2:8c:86:37:42:4a:4e:1c:37:1e:
+ 8d:66:ba:24:81:c9:4f:12:0f:21:c0:03:97:86:25:6d:5d:d3:
+ 22:29:a8:6c:a2:0d:a9:eb:3d:06:5b:99:3a:c7:cc:c3:9a:34:
+ 7f:ab:0e:c8:4e:1c:e1:fa:e4:dc:cd:0d:be:bf:24:fe:6c:e7:
+ 6b:c2:0d:c8:06:9e:4e:8d:61:28:a6:6a:fd:e5:f6:62:ea:18:
+ 3c:4e:a0:53:9d:b2:3a:9c:eb:a5:9c:91:16:b6:4d:82:e0:0c:
+ 05:48:a9:6c:f5:cc:f8:cb:9d:49:b4:f0:02:a5:fd:70:03:ed:
+ 8a:21:a5:ae:13:86:49:c3:33:73:be:87:3b:74:8b:17:45:26:
+ 4c:16:91:83:fe:67:7d:cd:4d:63:67:fa:f3:03:12:96:78:06:
+ 8d:b1:67:ed:8e:3f:be:9f:4f:02:f5:b3:09:2f:f3:4c:87:df:
+ 2a:cb:95:7c:01:cc:ac:36:7a:bf:a2:73:7a:f7:8f:c1:b5:9a:
+ a1:14:b2:8f:33:9f:0d:ef:22:dc:66:7b:84:bd:45:17:06:3d:
+ 3c:ca:b9:77:34:8f:ca:ea:cf:3f:31:3e:e3:88:e3:80:49:25:
+ c8:97:b5:9d:9a:99:4d:b0:3c:f8:4a:00:9b:64:dd:9f:39:4b:
+ d1:27:d7:b8
+SHA1 Fingerprint=FE:45:65:9B:79:03:5B:98:A1:61:B5:51:2E:AC:DA:58:09:48:22:4D
diff --git a/luni/src/main/files/cacerts/b3fb433b.0 b/luni/src/main/files/cacerts/b3fb433b.0
new file mode 100644
index 0000000..de880c1
--- /dev/null
+++ b/luni/src/main/files/cacerts/b3fb433b.0
@@ -0,0 +1,56 @@
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG
+A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3
+d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu
+dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq
+RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy
+MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD
+VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
+L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g
+Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi
+A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt
+ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH
+Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
+BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC
+R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX
+hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ a6:8b:79:29:00:00:00:00:50:d0:91:f9
+ Signature Algorithm: ecdsa-with-SHA384
+ Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - EC1
+ Validity
+ Not Before: Dec 18 15:25:36 2012 GMT
+ Not After : Dec 18 15:55:36 2037 GMT
+ Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - EC1
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (384 bit)
+ pub:
+ 04:84:13:c9:d0:ba:6d:41:7b:e2:6c:d0:eb:55:5f:
+ 66:02:1a:24:f4:5b:89:69:47:e3:b8:c2:7d:f1:f2:
+ 02:c5:9f:a0:f6:5b:d5:8b:06:19:86:4f:53:10:6d:
+ 07:24:27:a1:a0:f8:d5:47:19:61:4c:7d:ca:93:27:
+ ea:74:0c:ef:6f:96:09:fe:63:ec:70:5d:36:ad:67:
+ 77:ae:c9:9d:7c:55:44:3a:a2:63:51:1f:f5:e3:62:
+ d4:a9:47:07:3e:cc:20
+ ASN1 OID: secp384r1
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ B7:63:E7:1A:DD:8D:E9:08:A6:55:83:A4:E0:6A:50:41:65:11:42:49
+ Signature Algorithm: ecdsa-with-SHA384
+ 30:64:02:30:61:79:d8:e5:42:47:df:1c:ae:53:99:17:b6:6f:
+ 1c:7d:e1:bf:11:94:d1:03:88:75:e4:8d:89:a4:8a:77:46:de:
+ 6d:61:ef:02:f5:fb:b5:df:cc:fe:4e:ff:fe:a9:e6:a7:02:30:
+ 5b:99:d7:85:37:06:b5:7b:08:fd:eb:27:8b:4a:94:f9:e1:fa:
+ a7:8e:26:08:e8:7c:92:68:6d:73:d8:6f:26:ac:21:02:b8:99:
+ b7:26:41:5b:25:60:ae:d0:48:1a:ee:06
+SHA1 Fingerprint=20:D8:06:40:DF:9B:25:F5:12:25:3A:11:EA:F7:59:8A:EB:14:B5:47
diff --git a/luni/src/main/files/cacerts/ee90b008.0 b/luni/src/main/files/cacerts/ee90b008.0
new file mode 100644
index 0000000..f017edc
--- /dev/null
+++ b/luni/src/main/files/cacerts/ee90b008.0
@@ -0,0 +1,119 @@
+-----BEGIN CERTIFICATE-----
+MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1
+OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG
+A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ
+JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD
+vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo
+D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/
+Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW
+RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK
+HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN
+nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM
+0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i
+UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9
+Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg
+TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
+AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL
+BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
+2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX
+UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl
+6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK
+9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ
+HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI
+wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY
+XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l
+IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo
+hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr
+so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 59 (0x3b)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IL, O=StartCom Ltd., CN=StartCom Certification Authority G2
+ Validity
+ Not Before: Jan 1 01:00:01 2010 GMT
+ Not After : Dec 31 23:59:01 2039 GMT
+ Subject: C=IL, O=StartCom Ltd., CN=StartCom Certification Authority G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:b6:89:36:5b:07:b7:20:36:bd:82:bb:e1:16:20:
+ 03:95:7a:af:0e:a3:55:c9:25:99:4a:c5:d0:56:41:
+ 87:90:4d:21:60:a4:14:87:3b:cd:fd:b2:3e:b4:67:
+ 03:6a:ed:e1:0f:4b:c0:91:85:70:45:e0:42:9e:de:
+ 29:23:d4:01:0d:a0:10:79:b8:db:03:bd:f3:a9:2f:
+ d1:c6:e0:0f:cb:9e:8a:14:0a:b8:bd:f6:56:62:f1:
+ c5:72:b6:32:25:d9:b2:f3:bd:65:c5:0d:2c:6e:d5:
+ 92:6f:18:8b:00:41:14:82:6f:40:20:26:7a:28:0f:
+ f5:1e:7f:27:f7:94:b1:37:3d:b7:c7:91:f7:e2:01:
+ ec:fd:94:89:e1:cc:6e:d3:36:d6:0a:19:79:ae:d7:
+ 34:82:65:ff:7c:42:bb:b6:dd:0b:a6:34:af:4b:60:
+ fe:7f:43:49:06:8b:8c:43:b8:56:f2:d9:7f:21:43:
+ 17:ea:a7:48:95:01:75:75:ea:2b:a5:43:95:ea:15:
+ 84:9d:08:8d:26:6e:55:9b:ab:dc:d2:39:d2:31:1d:
+ 60:e2:ac:cc:56:45:24:f5:1c:54:ab:ee:86:dd:96:
+ 32:85:f8:4c:4f:e8:95:76:b6:05:dd:36:23:67:bc:
+ ff:15:e2:ca:3b:e6:a6:ec:3b:ec:26:11:34:48:8d:
+ f6:80:2b:1a:23:02:eb:8a:1c:3a:76:2a:7b:56:16:
+ 1c:72:2a:b3:aa:e3:60:a5:00:9f:04:9b:e2:6f:1e:
+ 14:58:5b:a5:6c:8b:58:3c:c3:ba:4e:3a:5c:f7:e1:
+ 96:2b:3e:ef:07:bc:a4:e5:5d:cc:4d:9f:0d:e1:dc:
+ aa:bb:e1:6e:1a:ec:8f:e1:b6:4c:4d:79:72:5d:17:
+ 35:0b:1d:d7:c1:47:da:96:24:e0:d0:72:a8:5a:5f:
+ 66:2d:10:dc:2f:2a:13:ae:26:fe:0a:1c:19:cc:d0:
+ 3e:0b:9c:c8:09:2e:f9:5b:96:7a:47:9c:e9:7a:f3:
+ 05:50:74:95:73:9e:30:09:f3:97:82:5e:e6:8f:39:
+ 08:1e:59:e5:35:14:42:13:ff:00:9c:f7:be:aa:50:
+ cf:e2:51:48:d7:b8:6f:af:f8:4e:7e:33:98:92:14:
+ 62:3a:75:63:cf:7b:fa:de:82:3b:a9:bb:39:e2:c4:
+ bd:2c:00:0e:c8:17:ac:13:ef:4d:25:8e:d8:b3:90:
+ 2f:a9:da:29:7d:1d:af:74:3a:b2:27:c0:c1:1e:3e:
+ 75:a3:16:a9:af:7a:22:5d:9f:13:1a:cf:a7:a0:eb:
+ e3:86:0a:d3:fd:e6:96:95:d7:23:c8:37:dd:c4:7c:
+ aa:36:ac:98:1a:12:b1:e0:4e:e8:b1:3b:f5:d6:6f:
+ f1:30:d7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 4B:C5:B4:40:6B:AD:1C:B3:A5:1C:65:6E:46:36:89:87:05:0C:0E:B6
+ Signature Algorithm: sha256WithRSAEncryption
+ 73:57:3f:2c:d5:95:32:7e:37:db:96:92:eb:19:5e:7e:53:e7:
+ 41:ec:11:b6:47:ef:b5:de:ed:74:5c:c5:f1:8e:49:e0:fc:6e:
+ 99:13:cd:9f:8a:da:cd:3a:0a:d8:3a:5a:09:3f:5f:34:d0:2f:
+ 03:d2:66:1d:1a:bd:9c:90:37:c8:0c:8e:07:5a:94:45:46:2a:
+ e6:be:7a:da:a1:a9:a4:69:12:92:b0:7d:36:d4:44:87:d7:51:
+ f1:29:63:d6:75:cd:16:e4:27:89:1d:f8:c2:32:48:fd:db:99:
+ d0:8f:5f:54:74:cc:ac:67:34:11:62:d9:0c:0a:37:87:d1:a3:
+ 17:48:8e:d2:17:1d:f6:d7:fd:db:65:eb:fd:a8:d4:f5:d6:4f:
+ a4:5b:75:e8:c5:d2:60:b2:db:09:7e:25:8b:7b:ba:52:92:9e:
+ 3e:e8:c5:77:a1:3c:e0:4a:73:6b:61:cf:86:dc:43:ff:ff:21:
+ fe:23:5d:24:4a:f5:d3:6d:0f:62:04:05:57:82:da:6e:a4:33:
+ 25:79:4b:2e:54:19:8b:cc:2c:3d:30:e9:d1:06:ff:e8:32:46:
+ be:b5:33:76:77:a8:01:5d:96:c1:c1:d5:be:ae:25:c0:c9:1e:
+ 0a:09:20:88:a1:0e:c9:f3:6f:4d:82:54:00:20:a7:d2:8f:e4:
+ 39:54:17:2e:8d:1e:b8:1b:bb:1b:bd:9a:4e:3b:10:34:dc:9c:
+ 88:53:ef:a2:31:5b:58:4f:91:62:c8:c2:9a:9a:cd:15:5d:38:
+ a9:d6:be:f8:13:b5:9f:12:69:f2:50:62:ac:fb:17:37:f4:ee:
+ b8:75:67:60:10:fb:83:50:f9:44:b5:75:9c:40:17:b2:fe:fd:
+ 79:5d:6e:58:58:5f:30:fc:00:ae:af:33:c1:0e:4e:6c:ba:a7:
+ a6:a1:7f:32:db:38:e0:b1:72:17:0a:2b:91:ec:6a:63:26:ed:
+ 89:d4:78:cc:74:1e:05:f8:6b:fe:8c:6a:76:39:29:ae:65:23:
+ 12:95:08:22:1c:97:ce:5b:06:ee:0c:e2:bb:bc:1f:44:93:f6:
+ d8:38:45:05:21:ed:e4:ad:ab:12:b6:03:a4:42:2e:2d:c4:09:
+ 3a:03:67:69:84:9a:e1:59:90:8a:28:85:d5:5d:74:b1:d1:0e:
+ 20:58:9b:13:a5:b0:63:a6:ed:7b:47:fd:45:55:30:a4:ee:9a:
+ d4:e6:e2:87:ef:98:c9:32:82:11:29:22:bc:00:0a:31:5e:2d:
+ 0f:c0:8e:e9:6b:b2:8f:2e:06:d8:d1:91:c7:c6:12:f4:4c:fd:
+ 30:17:c3:c1:da:38:5b:e3:a9:ea:e6:a1:ba:79:ef:73:d8:b6:
+ 53:57:2d:f6:d0:e1:d7:48
+SHA1 Fingerprint=31:F1:FD:68:22:63:20:EE:C6:3B:3F:9D:EA:4A:3E:53:7C:7C:39:17
diff --git a/luni/src/main/java/java/lang/reflect/Modifier.java b/luni/src/main/java/java/lang/reflect/Modifier.java
index fdbe3bb..5f973d5 100644
--- a/luni/src/main/java/java/lang/reflect/Modifier.java
+++ b/luni/src/main/java/java/lang/reflect/Modifier.java
@@ -109,6 +109,13 @@
public static final int MIRANDA = 0x8000;
/**
+ * Dex addition to mark instance constructors and static class
+ * initializer methods.
+ * @hide
+ */
+ public static final int CONSTRUCTOR = 0x10000;
+
+ /**
* Constructs a new {@code Modifier} instance.
*/
public Modifier() {
@@ -239,6 +246,14 @@
}
/**
+ * Returns true if the given modifiers contain {@link Modifier#CONSTRUCTOR}.
+ * @hide
+ */
+ public static boolean isConstructor(int modifiers) {
+ return ((modifiers & Modifier.CONSTRUCTOR) != 0);
+ }
+
+ /**
* Returns a string containing the string representation of all modifiers
* present in the specified modifiers. Modifiers appear in the order
* specified by the Java Language Specification.
diff --git a/luni/src/main/java/java/net/HttpURLConnection.java b/luni/src/main/java/java/net/HttpURLConnection.java
index d6b2435..89a4bc4 100644
--- a/luni/src/main/java/java/net/HttpURLConnection.java
+++ b/luni/src/main/java/java/net/HttpURLConnection.java
@@ -235,9 +235,9 @@
* until a connection is established.
*
* <h3>Response Caching</h3>
- * Android 4.0 (Ice Cream Sandwich) includes a response cache. See {@code
- * android.net.http.HttpResponseCache} for instructions on enabling HTTP caching
- * in your application.
+ * Android 4.0 (Ice Cream Sandwich, API level 15) includes a response cache. See
+ * {@code android.net.http.HttpResponseCache} for instructions on enabling HTTP
+ * caching in your application.
*
* <h3>Avoiding Bugs In Earlier Releases</h3>
* Prior to Android 2.2 (Froyo), this class had some frustrating bugs. In
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index d369970..cd3663d 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -94,7 +94,7 @@
* <p>Fractional seconds are handled specially: they're zero-padded on the <i>right</i>.
*
* <p>The two pattern characters {@code L} and {@code c} are ICU-compatible extensions, not
- * available in the RI or in Android before Android 2.3 "Gingerbread" (API level 9). These
+ * available in the RI or in Android before Android 2.3 (Gingerbread, API level 9). These
* extensions are necessary for correct localization in languages such as Russian
* that make a grammatical distinction between, say, the word "June" in the sentence "June" and
* in the sentence "June 10th"; the former is the stand-alone form, the latter the regular
@@ -102,7 +102,7 @@
* and {@code c} is equivalent, but for weekday names.
*
* <p>Five-count patterns (such as "MMMMM") used for the shortest non-numeric
- * representation of a field were introduced in Jelly Bean MR2 (API level 18).
+ * representation of a field were introduced in Android 4.3 (Jelly Bean MR2, API level 18).
*
* <p>When two numeric fields are directly adjacent with no intervening delimiter
* characters, they constitute a run of adjacent numeric fields. Such runs are
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index 23db9dc..fc8f7c6 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -66,12 +66,34 @@
* <p>Here are the versions of ICU (and the corresponding CLDR and Unicode versions) used in
* various Android releases:
* <table BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
- * <tr><td>Cupcake/Donut/Eclair</td> <td>ICU 3.8</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-5">CLDR 1.5</a></td> <td><a href="http://www.unicode.org/versions/Unicode5.0.0/">Unicode 5.0</a></td></tr>
- * <tr><td>Froyo</td> <td>ICU 4.2</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-7">CLDR 1.7</a></td> <td><a href="http://www.unicode.org/versions/Unicode5.1.0/">Unicode 5.1</a></td></tr>
- * <tr><td>Gingerbread/Honeycomb</td><td>ICU 4.4</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-8">CLDR 1.8</a></td> <td><a href="http://www.unicode.org/versions/Unicode5.2.0/">Unicode 5.2</a></td></tr>
- * <tr><td>Ice Cream Sandwich</td> <td>ICU 4.6</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-9">CLDR 1.9</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
- * <tr><td>Jelly Bean</td> <td>ICU 4.8</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-2-0">CLDR 2.0</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
- * <tr><td>Jelly Bean MR2</td> <td>ICU 50</td> <td><a href="http://cldr.unicode.org/index/downloads/cldr-21-1">CLDR 22.1</a></td> <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
+ * <tr><td>Android 1.5 (Cupcake)/Android 1.6 (Donut)/Android 2.0 (Eclair)</td>
+ * <td>ICU 3.8</td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-5">CLDR 1.5</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode5.0.0/">Unicode 5.0</a></td></tr>
+ * <tr><td>Android 2.2 (Froyo)</td>
+ * <td>ICU 4.2</td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-7">CLDR 1.7</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode5.1.0/">Unicode 5.1</a></td></tr>
+ * <tr><td>Android 2.3 (Gingerbread)/Android 3.0 (Honeycomb)</td>
+ * <td>ICU 4.4</td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-8">CLDR 1.8</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode5.2.0/">Unicode 5.2</a></td></tr>
+ * <tr><td>Android 4.0 (Ice Cream Sandwich)</td>
+ * <td>ICU 4.6</td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-1-9">CLDR 1.9</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
+ * <tr><td>Android 4.1 (Jelly Bean)</td>
+ * <td>ICU 4.8</td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-2-0">CLDR 2.0</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode6.0.0/">Unicode 6.0</a></td></tr>
+ * <tr><td>Android 4.3 (Jelly Bean MR2)</td>
+ * <td>ICU 50</td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-22-1">CLDR 22.1</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
+ * <tr><td>Android 4.4 (KitKat)</td>
+ * <td>ICU 51</td>
+ * <td><a href="http://cldr.unicode.org/index/downloads/cldr-23">CLDR 23</a></td>
+ * <td><a href="http://www.unicode.org/versions/Unicode6.2.0/">Unicode 6.2</a></td></tr>
* </table>
*
* <a name="default_locale"><h3>Be wary of the default locale</h3></a>
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index e4146bd..f64c717 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -21,6 +21,7 @@
import java.io.InputStream;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -342,19 +343,21 @@
/*
* Internal constructor. Creates a new ZipEntry by reading the
- * Central Directory Entry from "in", which must be positioned at
- * the CDE signature.
+ * Central Directory Entry (CDE) from "in", which must be positioned
+ * at the CDE signature.
*
- * On exit, "in" will be positioned at the start of the next entry.
+ * On exit, "in" will be positioned at the start of the next entry
+ * in the Central Directory.
*/
- ZipEntry(byte[] hdrBuf, InputStream in) throws IOException {
- Streams.readFully(in, hdrBuf, 0, hdrBuf.length);
+ ZipEntry(byte[] cdeHdrBuf, InputStream cdStream) throws IOException {
+ Streams.readFully(cdStream, cdeHdrBuf, 0, cdeHdrBuf.length);
- BufferIterator it = HeapBufferIterator.iterator(hdrBuf, 0, hdrBuf.length, ByteOrder.LITTLE_ENDIAN);
+ BufferIterator it = HeapBufferIterator.iterator(cdeHdrBuf, 0, cdeHdrBuf.length,
+ ByteOrder.LITTLE_ENDIAN);
int sig = it.readInt();
if (sig != CENSIG) {
- throw new ZipException("Central Directory Entry not found");
+ ZipFile.throwZipException("Central Directory Entry", sig);
}
it.seek(8);
@@ -382,20 +385,32 @@
localHeaderRelOffset = ((long) it.readInt()) & 0xffffffffL;
byte[] nameBytes = new byte[nameLength];
- Streams.readFully(in, nameBytes, 0, nameBytes.length);
+ Streams.readFully(cdStream, nameBytes, 0, nameBytes.length);
+ if (containsNulByte(nameBytes)) {
+ throw new ZipException("Filename contains NUL byte: " + Arrays.toString(nameBytes));
+ }
name = new String(nameBytes, 0, nameBytes.length, StandardCharsets.UTF_8);
if (extraLength > 0) {
extra = new byte[extraLength];
- Streams.readFully(in, extra, 0, extraLength);
+ Streams.readFully(cdStream, extra, 0, extraLength);
}
// The RI has always assumed UTF-8. (If GPBF_UTF8_FLAG isn't set, the encoding is
// actually IBM-437.)
if (commentByteCount > 0) {
byte[] commentBytes = new byte[commentByteCount];
- Streams.readFully(in, commentBytes, 0, commentByteCount);
+ Streams.readFully(cdStream, commentBytes, 0, commentByteCount);
comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
}
}
+
+ private static boolean containsNulByte(byte[] bytes) {
+ for (byte b : bytes) {
+ if (b == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index 2f9e3b0..c25bbc1 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -20,7 +20,6 @@
import dalvik.system.CloseGuard;
import java.io.BufferedInputStream;
import java.io.Closeable;
-import java.io.EOFException;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
@@ -40,7 +39,7 @@
* the zip file's central directory up front (from the constructor), but if you're using
* {@link #getEntry} to look up multiple files by name, you get the benefit of this index.
*
- * <p>If you only want to iterate through all the files (using {@link #entries}, you should
+ * <p>If you only want to iterate through all the files (using {@link #entries()}, you should
* consider {@link ZipInputStream}, which provides stream-like read access to a zip file and
* has a lower up-front cost because you don't pay to build an in-memory index.
*
@@ -274,30 +273,41 @@
RandomAccessFile localRaf = raf;
synchronized (localRaf) {
// We don't know the entry data's start position. All we have is the
- // position of the entry's local header. At position 6 we find the
- // General Purpose Bit Flag.
+ // position of the entry's local header.
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
- RAFStream rafStream= new RAFStream(localRaf, entry.localHeaderRelOffset + 6);
+ RAFStream rafStream = new RAFStream(localRaf, entry.localHeaderRelOffset);
DataInputStream is = new DataInputStream(rafStream);
+
+ final int localMagic = Integer.reverseBytes(is.readInt());
+ if (localMagic != LOCSIG) {
+ throwZipException("Local File Header", localMagic);
+ }
+
+ is.skipBytes(2);
+
+ // At position 6 we find the General Purpose Bit Flag.
int gpbf = Short.reverseBytes(is.readShort()) & 0xffff;
if ((gpbf & ZipFile.GPBF_UNSUPPORTED_MASK) != 0) {
throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf);
}
- // At position 28 we find the length of the extra data. In some cases
- // this length differs from the one coming in the central header.
- is.skipBytes(20);
- int localExtraLenOrWhatever = Short.reverseBytes(is.readShort()) & 0xffff;
+ // Offset 26 has the file name length, and offset 28 has the extra field length.
+ // These lengths can differ from the ones in the central header.
+ is.skipBytes(18);
+ int fileNameLength = Short.reverseBytes(is.readShort()) & 0xffff;
+ int extraFieldLength = Short.reverseBytes(is.readShort()) & 0xffff;
is.close();
- // Skip the name and this "extra" data or whatever it is:
- rafStream.skip(entry.nameLength + localExtraLenOrWhatever);
- rafStream.length = rafStream.offset + entry.compressedSize;
- if (entry.compressionMethod == ZipEntry.DEFLATED) {
- int bufSize = Math.max(1024, (int)Math.min(entry.getSize(), 65535L));
- return new ZipInflaterInputStream(rafStream, new Inflater(true), bufSize, entry);
- } else {
+ // Skip the variable-size file name and extra field data.
+ rafStream.skip(fileNameLength + extraFieldLength);
+
+ if (entry.compressionMethod == ZipEntry.STORED) {
+ rafStream.endOffset = rafStream.offset + entry.size;
return rafStream;
+ } else {
+ rafStream.endOffset = rafStream.offset + entry.compressedSize;
+ int bufSize = Math.max(1024, (int) Math.min(entry.getSize(), 65535L));
+ return new ZipInflaterInputStream(rafStream, new Inflater(true), bufSize, entry);
}
}
}
@@ -345,21 +355,26 @@
throw new ZipException("File too short to be a zip file: " + raf.length());
}
+ raf.seek(0);
+ final int headerMagic = Integer.reverseBytes(raf.readInt());
+ if (headerMagic != LOCSIG) {
+ throw new ZipException("Not a zip archive");
+ }
+
long stopOffset = scanOffset - 65536;
if (stopOffset < 0) {
stopOffset = 0;
}
- final int ENDHEADERMAGIC = 0x06054b50;
while (true) {
raf.seek(scanOffset);
- if (Integer.reverseBytes(raf.readInt()) == ENDHEADERMAGIC) {
+ if (Integer.reverseBytes(raf.readInt()) == ENDSIG) {
break;
}
scanOffset--;
if (scanOffset < stopOffset) {
- throw new ZipException("EOCD not found; not a zip file?");
+ throw new ZipException("End Of Central Directory signature not found");
}
}
@@ -379,7 +394,7 @@
int commentLength = it.readShort() & 0xffff;
if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
- throw new ZipException("spanned archives not supported");
+ throw new ZipException("Spanned archives not supported");
}
if (commentLength > 0) {
@@ -397,6 +412,9 @@
byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry.
for (int i = 0; i < numEntries; ++i) {
ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream);
+ if (newEntry.localHeaderRelOffset >= centralDirOffset) {
+ throw new ZipException("Local file header offset is after central directory");
+ }
String entryName = newEntry.getName();
if (entries.put(entryName, newEntry) != null) {
throw new ZipException("Duplicate entry name: " + entryName);
@@ -404,6 +422,11 @@
}
}
+ static void throwZipException(String msg, int magic) throws ZipException {
+ final String hexString = IntegralToString.intToHexString(magic, true, 8);
+ throw new ZipException(msg + " signature not found; was " + hexString);
+ }
+
/**
* Wrap a stream around a RandomAccessFile. The RandomAccessFile is shared
* among all streams returned by getInputStream(), so we have to synchronize
@@ -414,17 +437,17 @@
*/
static class RAFStream extends InputStream {
private final RandomAccessFile sharedRaf;
- private long length;
+ private long endOffset;
private long offset;
public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException {
sharedRaf = raf;
offset = initialOffset;
- length = raf.length();
+ endOffset = raf.length();
}
@Override public int available() throws IOException {
- return (offset < length ? 1 : 0);
+ return (offset < endOffset ? 1 : 0);
}
@Override public int read() throws IOException {
@@ -433,10 +456,11 @@
@Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
synchronized (sharedRaf) {
- sharedRaf.seek(offset);
- if (byteCount > length - offset) {
- byteCount = (int) (length - offset);
+ final long length = endOffset - offset;
+ if (byteCount > length) {
+ byteCount = (int) length;
}
+ sharedRaf.seek(offset);
int count = sharedRaf.read(buffer, byteOffset, byteCount);
if (count > 0) {
offset += count;
@@ -448,8 +472,8 @@
}
@Override public long skip(long byteCount) throws IOException {
- if (byteCount > length - offset) {
- byteCount = length - offset;
+ if (byteCount > endOffset - offset) {
+ byteCount = endOffset - offset;
}
offset += byteCount;
return byteCount;
@@ -457,7 +481,7 @@
public int fill(Inflater inflater, int nativeEndBufSize) throws IOException {
synchronized (sharedRaf) {
- int len = Math.min((int) (length - offset), nativeEndBufSize);
+ int len = Math.min((int) (endOffset - offset), nativeEndBufSize);
int cnt = inflater.setFileInput(sharedRaf.getFD(), offset, nativeEndBufSize);
// setFileInput read from the file, so we need to get the OS and RAFStream back
// in sync...
@@ -477,8 +501,19 @@
}
@Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- int i = super.read(buffer, byteOffset, byteCount);
- if (i != -1) {
+ final int i;
+ try {
+ i = super.read(buffer, byteOffset, byteCount);
+ } catch (IOException e) {
+ throw new IOException("Error reading data for " + entry.getName() + " near offset "
+ + bytesRead, e);
+ }
+ if (i == -1) {
+ if (entry.size != bytesRead) {
+ throw new IOException("Size mismatch on inflated file: " + bytesRead + " vs "
+ + entry.size);
+ }
+ } else {
bytesRead += i;
}
return i;
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index 34c2c6f..994ae90 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -62,6 +62,7 @@
add("application/pgp-keys", "key");
add("application/pgp-signature", "pgp");
add("application/pics-rules", "prf");
+ add("application/pkix-cert", "cer");
add("application/rar", "rar");
add("application/rdf+xml", "rdf");
add("application/rss+xml", "rss");
@@ -183,6 +184,7 @@
add("application/x-nwc", "nwc");
add("application/x-object", "o");
add("application/x-oz-application", "oza");
+ add("application/x-pem-file", "pem");
add("application/x-pkcs12", "p12");
add("application/x-pkcs12", "pfx");
add("application/x-pkcs7-certreqresp", "p7r");
@@ -206,6 +208,7 @@
add("application/x-webarchive-xml", "webarchivexml");
add("application/x-x509-ca-cert", "crt");
add("application/x-x509-user-cert", "crt");
+ add("application/x-x509-server-cert", "crt");
add("application/x-xcf", "xcf");
add("application/x-xfig", "fig");
add("application/xhtml+xml", "xhtml");
diff --git a/luni/src/main/java/libcore/reflect/AnnotationAccess.java b/luni/src/main/java/libcore/reflect/AnnotationAccess.java
index fe740de..064151e 100644
--- a/luni/src/main/java/libcore/reflect/AnnotationAccess.java
+++ b/luni/src/main/java/libcore/reflect/AnnotationAccess.java
@@ -187,11 +187,25 @@
Dex dex = dexClass.getDex();
int annotationTypeIndex = getTypeIndex(dex, annotationClass);
if (annotationTypeIndex == -1) {
- return null; // the dex file doesn't use this annotation
+ return null; // The dex file doesn't use this annotation.
}
int annotationSetOffset = getAnnotationSetOffset(element);
- return getAnnotationFromAnnotationSet(dex, annotationSetOffset, annotationTypeIndex);
+ if (annotationSetOffset == 0) {
+ return null; // no annotation
+ }
+
+ Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item
+ for (int i = 0, size = setIn.readInt(); i < size; i++) {
+ int annotationOffset = setIn.readInt();
+ Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item
+ com.android.dex.Annotation candidate = annotationIn.readAnnotation();
+ if (candidate.getTypeIndex() == annotationTypeIndex) {
+ return candidate;
+ }
+ }
+
+ return null; // This set doesn't contain the annotation.
}
/**
@@ -199,7 +213,7 @@
*/
private static int getAnnotationSetOffset(AnnotatedElement element) {
Class<?> dexClass = getDexClass(element);
- int directoryOffset = getDirectoryOffset(dexClass);
+ int directoryOffset = dexClass.getDexAnnotationDirectoryOffset();
if (directoryOffset == 0) {
return 0; // nothing on this class has annotations
}
@@ -255,38 +269,32 @@
Dex dex = declaringClass.getDex();
int declaringClassIndex = getTypeIndex(dex, declaringClass);
int typeIndex = getTypeIndex(dex, type);
- int nameIndex = getStringIndex(dex, name);
+ int nameIndex = dex.findStringIndex(name);
FieldId fieldId = new FieldId(dex, declaringClassIndex, typeIndex, nameIndex);
- return Collections.binarySearch(dex.fieldIds(), fieldId);
+ return dex.findFieldIndex(fieldId);
}
public static int getMethodIndex(Class<?> declaringClass, String name, int protoIndex) {
Dex dex = declaringClass.getDex();
int declaringClassIndex = getTypeIndex(dex, declaringClass);
- int nameIndex = getStringIndex(dex, name);
+ int nameIndex = dex.findStringIndex(name);
MethodId methodId = new MethodId(dex, declaringClassIndex, protoIndex, nameIndex);
- return Collections.binarySearch(dex.methodIds(), methodId);
+ return dex.findMethodIndex(methodId);
}
/**
* Returns the parameter annotations on {@code member}.
*/
- public static Annotation[][] getParameterAnnotations(Member member) {
- Class<?> declaringClass = member.getDeclaringClass();
+ public static Annotation[][] getParameterAnnotations(Class<?> declaringClass,
+ int methodDexIndex) {
Dex dex = declaringClass.getDex();
- int methodDexIndex;
- if (member instanceof Method) {
- methodDexIndex = ((Method) member).getDexMethodIndex();
- } else {
- methodDexIndex = ((Constructor<?>) member).getDexMethodIndex();
- }
int protoIndex = dex.methodIds().get(methodDexIndex).getProtoIndex();
ProtoId proto = dex.protoIds().get(protoIndex);
TypeList parametersList = dex.readTypeList(proto.getParametersOffset());
short[] types = parametersList.getTypes();
int typesCount = types.length;
- int directoryOffset = getDirectoryOffset(declaringClass);
+ int directoryOffset = declaringClass.getDexAnnotationDirectoryOffset();
if (directoryOffset == 0) {
return new Annotation[typesCount][0]; // nothing on this class has annotations
}
@@ -358,7 +366,7 @@
throw new AssertionError("annotation value type != annotation class");
}
- int methodNameIndex = Collections.binarySearch(dex.strings(), method.getName());
+ int methodNameIndex = dex.findStringIndex(method.getName());
for (int i = 0; i < fieldCount; i++) {
int candidateNameIndex = reader.readAnnotationName();
if (candidateNameIndex == methodNameIndex) {
@@ -389,7 +397,7 @@
if (reader == null) {
return null;
}
- return indexToType(c, dex, reader.readType());
+ return c.getDexCacheType(dex, reader.readType());
}
public static AccessibleObject getEnclosingMethodOrConstructor(Class<?> c) {
@@ -529,47 +537,21 @@
* was derived.
*/
+ /** Find dex's type index for the class c */
private static int getTypeIndex(Dex dex, Class<?> c) {
- return dex == c.getDex() ? c.getTypeIndex() : computeTypeIndex(dex, c);
- }
-
- public static int computeTypeIndex(Dex dex, Class<?> c) {
+ if (dex == c.getDex()) {
+ return c.getDexTypeIndex();
+ }
if (dex == null) {
return -1;
}
- int typeIndex = Collections.binarySearch(dex.typeNames(), InternalNames.getInternalName(c));
+ int typeIndex = dex.findTypeIndex(InternalNames.getInternalName(c));
if (typeIndex < 0) {
typeIndex = -1;
}
return typeIndex;
}
- private static int getStringIndex(Dex dex, String string) {
- return Collections.binarySearch(dex.strings(), string);
- }
-
- private static int getDirectoryOffset(Class<?> c) {
- return c.getAnnotationDirectoryOffset();
- }
-
- private static com.android.dex.Annotation getAnnotationFromAnnotationSet(
- Dex dex, int annotationSetOffset, int annotationType) {
- if (annotationSetOffset == 0) {
- return null; // no annotation
- }
-
- Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item
- for (int i = 0, size = setIn.readInt(); i < size; i++) {
- int annotationOffset = setIn.readInt();
- Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item
- com.android.dex.Annotation candidate = annotationIn.readAnnotation();
- if (candidate.getTypeIndex() == annotationType) {
- return candidate;
- }
- }
-
- return null; // this set doesn't carry the annotation
- }
private static EncodedValueReader getAnnotationReader(
Dex dex, AnnotatedElement element, String annotationName, int expectedFieldCount) {
@@ -578,16 +560,28 @@
return null; // no annotations on the class
}
- int annotationTypeIndex = Collections.binarySearch(dex.typeNames(), annotationName);
- com.android.dex.Annotation annotation = getAnnotationFromAnnotationSet(
- dex, annotationSetOffset, annotationTypeIndex);
+ Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item
+ com.android.dex.Annotation annotation = null;
+ // TODO: is it better to compute the index of the annotation name in the dex file and check
+ // indices below?
+ for (int i = 0, size = setIn.readInt(); i < size; i++) {
+ int annotationOffset = setIn.readInt();
+ Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item
+ com.android.dex.Annotation candidate = annotationIn.readAnnotation();
+ String candidateAnnotationName = dex.typeNames().get(candidate.getTypeIndex());
+ if (annotationName.equals(candidateAnnotationName)) {
+ annotation = candidate;
+ break;
+ }
+ }
if (annotation == null) {
return null; // no annotation
}
EncodedValueReader reader = annotation.getReader();
int fieldCount = reader.readAnnotation();
- if (reader.getAnnotationType() != annotationTypeIndex) {
+ String readerAnnotationName = dex.typeNames().get(reader.getAnnotationType());
+ if (!readerAnnotationName.equals(annotationName)) {
throw new AssertionError();
}
if (fieldCount != expectedFieldCount) {
@@ -615,8 +609,8 @@
int typeIndex) {
try {
@SuppressWarnings("unchecked") // we do a runtime check
- Class<? extends Annotation> result = (Class<? extends Annotation>) indexToType(context,
- dex, typeIndex);
+ Class<? extends Annotation> result =
+ (Class<? extends Annotation>) context.getDexCacheType(dex, typeIndex);
if (!result.isAnnotation()) {
throw new IncompatibleClassChangeError("Expected annotation: " + result.getName());
}
@@ -626,65 +620,23 @@
}
}
- private static Class<?> indexToType(Class<?> context, Dex dex, int typeIndex) {
- String internalName = dex.typeNames().get(typeIndex);
- return InternalNames.getClass(context.getClassLoader(), internalName);
- }
-
private static AccessibleObject indexToMethod(Class<?> context, Dex dex, int methodIndex) {
- MethodId methodId = dex.methodIds().get(methodIndex);
- Class<?> declaringClass = indexToType(context, dex, methodId.getDeclaringClassIndex());
- String name = dex.strings().get(methodId.getNameIndex());
- Class<?>[] parametersArray = protoIndexToParameters(context, dex, methodId.getProtoIndex());
- try {
- return name.equals("<init>")
- ? declaringClass.getDeclaredConstructor(parametersArray)
- : declaringClass.getDeclaredMethod(name, parametersArray);
- } catch (NoSuchMethodException e) {
- throw new IncompatibleClassChangeError("Couldn't find " + declaringClass.getName()
- + "." + name + Arrays.toString(parametersArray));
- }
- }
-
- public static Class<?>[] protoIndexToParameters(Class<?> context, Dex dex, int protoIndex) {
- ProtoId proto = dex.protoIds().get(protoIndex);
- TypeList parametersList = dex.readTypeList(proto.getParametersOffset());
- short[] types = parametersList.getTypes();
+ Class<?> declaringClass =
+ context.getDexCacheType(dex, dex.declaringClassIndexFromMethodIndex(methodIndex));
+ String name = context.getDexCacheString(dex, dex.nameIndexFromMethodIndex(methodIndex));
+ short[] types = dex.parameterTypeIndicesFromMethodIndex(methodIndex);
Class<?>[] parametersArray = new Class[types.length];
for (int i = 0; i < types.length; i++) {
- parametersArray[i] = indexToType(context, dex, types[i]);
+ parametersArray[i] = context.getDexCacheType(dex, types[i]);
}
- return parametersArray;
- }
-
- public static Class<?>[] typeIndexToInterfaces(Class<?> context, Dex dex, int typeIndex) {
- ClassDef def = getClassDef(dex, typeIndex);
- if (def == null) {
- return EmptyArray.CLASS;
+ try {
+ return name.equals("<init>")
+ ? declaringClass.getDeclaredConstructor(parametersArray)
+ : declaringClass.getDeclaredMethod(name, parametersArray);
+ } catch (NoSuchMethodException e) {
+ throw new IncompatibleClassChangeError("Couldn't find " + declaringClass.getName()
+ + "." + name + Arrays.toString(parametersArray));
}
- short[] interfaces = def.getInterfaces();
- Class<?>[] result = new Class<?>[interfaces.length];
- for (int i = 0; i < interfaces.length; i++) {
- result[i] = indexToType(context, dex, interfaces[i]);
- }
- return result;
- }
-
- public static int typeIndexToAnnotationDirectoryOffset(Dex dex, int typeIndex) {
- ClassDef def = getClassDef(dex, typeIndex);
- return def == null ? 0 : def.getAnnotationsOffset();
- }
-
- private static ClassDef getClassDef(Dex dex, int typeIndex) {
- if (typeIndex == -1) {
- return null;
- }
- for (ClassDef def : dex.classDefs()) {
- if (def.getTypeIndex() == typeIndex) {
- return def;
- }
- }
- throw new AssertionError();
}
private static List<Annotation> annotationSetToAnnotations(Class<?> context, int offset) {
@@ -722,7 +674,7 @@
private static <A extends Annotation> A toAnnotationInstance(Class<?> context, Dex dex,
Class<A> annotationClass, EncodedValueReader reader) {
int fieldCount = reader.readAnnotation();
- if (annotationClass != indexToType(context, dex, reader.getAnnotationType())) {
+ if (annotationClass != context.getDexCacheType(dex, reader.getAnnotationType())) {
throw new AssertionError("annotation value type != return type");
}
AnnotationMember[] members = new AnnotationMember[fieldCount];
@@ -766,10 +718,10 @@
return toAnnotationInstance(context, dex, annotationClass, reader);
} else if (type == String.class) {
int index = reader.readString();
- return dex.strings().get(index);
+ return context.getDexCacheString(dex, index);
} else if (type == Class.class) {
int index = reader.readType();
- return indexToType(context, dex, index);
+ return context.getDexCacheType(dex, index);
} else if (type == byte.class) {
return reader.readByte();
} else if (type == short.class) {
diff --git a/luni/src/main/java/libcore/reflect/GenericSignatureParser.java b/luni/src/main/java/libcore/reflect/GenericSignatureParser.java
index 0c94eba..5e86c49 100644
--- a/luni/src/main/java/libcore/reflect/GenericSignatureParser.java
+++ b/luni/src/main/java/libcore/reflect/GenericSignatureParser.java
@@ -22,6 +22,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
+import libcore.util.EmptyArray;
/**
* Implements a parser for the generics signature attribute.
@@ -119,19 +120,23 @@
* @param genericDecl the GenericDeclaration calling this method
* @param signature the generic signature of the class
*/
- public void parseForClass(GenericDeclaration genericDecl,
- String signature) {
+ public void parseForClass(GenericDeclaration genericDecl, String signature) {
setInput(genericDecl, signature);
if (!eof) {
parseClassSignature();
} else {
if(genericDecl instanceof Class) {
Class c = (Class) genericDecl;
- this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
this.superclassType = c.getSuperclass();
- this.interfaceTypes = new ListOfTypes(c.getInterfaces());
+ Class<?>[] interfaces = c.getInterfaces();
+ if (interfaces.length == 0) {
+ this.interfaceTypes = ListOfTypes.EMPTY;
+ } else {
+ this.interfaceTypes = new ListOfTypes(interfaces);
+ }
} else {
- this.formalTypeParameters = ListOfVariables.EMPTY;
+ this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
this.superclassType = Object.class;
this.interfaceTypes = ListOfTypes.EMPTY;
}
@@ -152,9 +157,19 @@
parseMethodTypeSignature(rawExceptionTypes);
} else {
Method m = (Method) genericDecl;
- this.formalTypeParameters = ListOfVariables.EMPTY;
- this.parameterTypes = new ListOfTypes(m.getParameterTypes());
- this.exceptionTypes = new ListOfTypes(m.getExceptionTypes());
+ this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ if (parameterTypes.length == 0) {
+ this.parameterTypes = ListOfTypes.EMPTY;
+ } else {
+ this.parameterTypes = new ListOfTypes(parameterTypes);
+ }
+ Class<?>[] exceptionTypes = m.getExceptionTypes();
+ if (exceptionTypes.length == 0) {
+ this.exceptionTypes = ListOfTypes.EMPTY;
+ } else {
+ this.exceptionTypes = new ListOfTypes(exceptionTypes);
+ }
this.returnType = m.getReturnType();
}
}
@@ -173,9 +188,19 @@
parseMethodTypeSignature(rawExceptionTypes);
} else {
Constructor c = (Constructor) genericDecl;
- this.formalTypeParameters = ListOfVariables.EMPTY;
- this.parameterTypes = new ListOfTypes(c.getParameterTypes());
- this.exceptionTypes = new ListOfTypes(c.getExceptionTypes());
+ this.formalTypeParameters = EmptyArray.TYPE_VARIABLE;
+ Class<?>[] parameterTypes = c.getParameterTypes();
+ if (parameterTypes.length == 0) {
+ this.parameterTypes = ListOfTypes.EMPTY;
+ } else {
+ this.parameterTypes = new ListOfTypes(parameterTypes);
+ }
+ Class<?>[] exceptionTypes = c.getExceptionTypes();
+ if (exceptionTypes.length == 0) {
+ this.exceptionTypes = ListOfTypes.EMPTY;
+ } else {
+ this.exceptionTypes = new ListOfTypes(exceptionTypes);
+ }
}
}
@@ -449,7 +474,7 @@
}
}
- boolean isStopSymbol(char ch) {
+ static boolean isStopSymbol(char ch) {
switch (ch) {
case ':':
case '/':
@@ -472,7 +497,7 @@
char ch = buffer[pos];
if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A') && (ch <= 'Z')
|| !isStopSymbol(ch)) {
- identBuf.append(buffer[pos]);
+ identBuf.append(ch);
pos++;
} else {
identifier = identBuf.toString();
diff --git a/luni/src/main/java/libcore/reflect/ListOfTypes.java b/luni/src/main/java/libcore/reflect/ListOfTypes.java
index c5d90e2..12ad84d 100644
--- a/luni/src/main/java/libcore/reflect/ListOfTypes.java
+++ b/luni/src/main/java/libcore/reflect/ListOfTypes.java
@@ -19,6 +19,7 @@
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
+import libcore.util.EmptyArray;
public final class ListOfTypes {
public static final ListOfTypes EMPTY = new ListOfTypes(0);
@@ -50,12 +51,20 @@
public Type[] getResolvedTypes() {
Type[] result = resolvedTypes;
- return result != null ? result : (resolvedTypes = resolveTypes(types));
+ if (result == null) {
+ result = resolveTypes(types);
+ resolvedTypes = result;
+ }
+ return result;
}
private Type[] resolveTypes(List<Type> unresolved) {
- Type[] result = new Type[unresolved.size()];
- for (int i = 0; i < unresolved.size(); i++) {
+ int size = unresolved.size();
+ if (size == 0) {
+ return EmptyArray.TYPE;
+ }
+ Type[] result = new Type[size];
+ for (int i = 0; i < size; i++) {
Type type = unresolved.get(i);
try {
result[i] = ((ParameterizedTypeImpl) type).getResolvedType();
diff --git a/luni/src/main/java/libcore/reflect/ListOfVariables.java b/luni/src/main/java/libcore/reflect/ListOfVariables.java
index 5d96817..43f2969 100644
--- a/luni/src/main/java/libcore/reflect/ListOfVariables.java
+++ b/luni/src/main/java/libcore/reflect/ListOfVariables.java
@@ -20,8 +20,6 @@
import java.util.ArrayList;
final class ListOfVariables {
- public static final TypeVariable[] EMPTY = new TypeVariableImpl[0];
-
final ArrayList<TypeVariable<?>> array = new ArrayList<TypeVariable<?>>();
void add (TypeVariable<?> elem) {
diff --git a/luni/src/main/java/libcore/reflect/Types.java b/luni/src/main/java/libcore/reflect/Types.java
index 2132b11..3181ca1 100644
--- a/luni/src/main/java/libcore/reflect/Types.java
+++ b/luni/src/main/java/libcore/reflect/Types.java
@@ -22,6 +22,7 @@
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
+import libcore.util.EmptyArray;
public final class Types {
private Types() {
@@ -42,16 +43,19 @@
PRIMITIVE_TO_SIGNATURE.put(boolean.class, "Z");
}
- public static Type[] getClonedTypeArray(ListOfTypes types) {
- return types.getResolvedTypes().clone();
+ public static Type[] getTypeArray(ListOfTypes types, boolean clone) {
+ if (types.length() == 0) {
+ return EmptyArray.TYPE;
+ }
+ Type[] result = types.getResolvedTypes();
+ return clone ? result.clone() : result;
}
public static Type getType(Type type) {
if (type instanceof ParameterizedTypeImpl) {
return ((ParameterizedTypeImpl)type).getResolvedType();
- } else {
- return type;
}
+ return type;
}
/**
diff --git a/luni/src/main/java/libcore/util/EmptyArray.java b/luni/src/main/java/libcore/util/EmptyArray.java
index 6c99878..1713bfc 100644
--- a/luni/src/main/java/libcore/util/EmptyArray.java
+++ b/luni/src/main/java/libcore/util/EmptyArray.java
@@ -30,4 +30,7 @@
public static final String[] STRING = new String[0];
public static final Throwable[] THROWABLE = new Throwable[0];
public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
+ public static final java.lang.reflect.Type[] TYPE = new java.lang.reflect.Type[0];
+ public static final java.lang.reflect.TypeVariable[] TYPE_VARIABLE =
+ new java.lang.reflect.TypeVariable[0];
}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
index 5760906..5c0e328 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
@@ -300,6 +300,11 @@
updateSeed(getRandomBytes(DIGEST_LENGTH));
nextBIndex = HASHBYTES_TO_USE;
+ // updateSeed(...) updates where the last word of the seed is, so we
+ // have to read it again.
+ lastWord = seed[BYTES_OFFSET] == 0 ? 0
+ : (seed[BYTES_OFFSET] + extrabytes) >> 3 - 1;
+
} else if (state == SET_SEED) {
System.arraycopy(seed, HASH_OFFSET, copies, HASHCOPY_OFFSET,
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index dec074e..60af4d0 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -33,7 +33,6 @@
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import junit.framework.TestCase;
-import libcore.io.IoUtils;
public final class ZipFileTest extends TestCase {
/**
@@ -119,6 +118,51 @@
}
}
+ /**
+ * Make sure the size used for stored zip entires is the uncompressed size.
+ * b/10227498
+ */
+ public void testStoredEntrySize() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream out = new ZipOutputStream(baos);
+
+ // Set up a single stored entry.
+ String name = "test_file";
+ int expectedLength = 5;
+ ZipEntry outEntry = new ZipEntry(name);
+ byte[] buffer = new byte[expectedLength];
+ outEntry.setMethod(ZipEntry.STORED);
+ CRC32 crc = new CRC32();
+ crc.update(buffer);
+ outEntry.setCrc(crc.getValue());
+ outEntry.setSize(buffer.length);
+
+ out.putNextEntry(outEntry);
+ out.write(buffer);
+ out.closeEntry();
+ out.close();
+
+ // Write the result to a file.
+ byte[] outBuffer = baos.toByteArray();
+ File zipFile = createTemporaryZipFile();
+ writeBytes(zipFile, outBuffer);
+
+ ZipFile zip = new ZipFile(zipFile);
+ // Set up the zip entry to have different compressed/uncompressed sizes.
+ ZipEntry ze = zip.getEntry(name);
+ ze.setCompressedSize(expectedLength - 1);
+ // Read the contents of the stream and verify uncompressed size was used.
+ InputStream stream = zip.getInputStream(ze);
+ int count = 0;
+ int read;
+ while ((read = stream.read(buffer)) != -1) {
+ count += read;
+ }
+
+ assertEquals(expectedLength, count);
+
+ }
+
public void testInflatingStreamsRequiringZipRefill() throws IOException {
int originalSize = 1024 * 1024;
byte[] readBuffer = new byte[8192];
@@ -379,33 +423,24 @@
assertEquals(null, zipFile.getComment());
}
- public void testNameLengthChecks() throws IOException {
- // Is entry name length checking done on bytes or characters?
- // Really it should be bytes, but the RI only checks characters at construction time.
- // Android does the same, because it's cheap...
- try {
- new ZipEntry((String) null);
- fail();
- } catch (NullPointerException expected) {
- }
- new ZipEntry(makeString(0xffff, "a"));
- try {
- new ZipEntry(makeString(0xffff + 1, "a"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
+ // https://code.google.com/p/android/issues/detail?id=58465
+ public void test_NUL_in_filename() throws Exception {
+ File file = createTemporaryZipFile();
- // ...but Android won't let you create a zip file with a truncated name.
- ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
- ZipEntry ze = new ZipEntry(makeString(0xffff, "\u0666"));
- try {
- out.putNextEntry(ze);
- fail(); // The RI fails this test; it just checks the character count at construction time.
- } catch (IllegalArgumentException expected) {
- }
- out.closeEntry();
- out.putNextEntry(new ZipEntry("okay")); // ZipOutputStream.close throws if you add nothing!
+ // We allow creation of a ZipEntry whose name contains a NUL byte,
+ // mainly because it's not likely to happen by accident and it's useful for testing.
+ ZipOutputStream out = createZipOutputStream(file);
+ out.putNextEntry(new ZipEntry("hello"));
+ out.putNextEntry(new ZipEntry("hello\u0000"));
out.close();
+
+ // But you can't open a ZIP file containing such an entry, because we reject it
+ // when we find it in the central directory.
+ try {
+ ZipFile zipFile = new ZipFile(file);
+ fail();
+ } catch (ZipException expected) {
+ }
}
public void testCrc() throws IOException {
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index 98cffc1..82294fe 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -16,8 +16,10 @@
package libcore.javax.net.ssl;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
@@ -87,6 +89,20 @@
boolean secureRenegotiation)
throws Exception {
TestSSLContext c = TestSSLContext.create(testKeyStore, testKeyStore);
+
+ // Create a TestSSLContext where the KeyManager returns wrong (randomly generated) private
+ // keys, matching the algorithm and parameters of the correct keys.
+ // I couldn't find a more elegant way to achieve this other than temporarily replacing the
+ // first element of TestKeyStore.keyManagers while invoking TestSSLContext.create.
+ TestSSLContext cWithWrongPrivateKeys;
+ {
+ KeyManager originalKeyManager = testKeyStore.keyManagers[0];
+ testKeyStore.keyManagers[0] =
+ new RandomPrivateKeyX509ExtendedKeyManager(c.serverKeyManager);
+ cWithWrongPrivateKeys = TestSSLContext.create(testKeyStore, testKeyStore);
+ testKeyStore.keyManagers[0] = originalKeyManager;
+ }
+
String[] cipherSuites = c.clientContext.createSSLEngine().getSupportedCipherSuites();
for (String cipherSuite : cipherSuites) {
boolean errorExpected = StandardNames.IS_RI && cipherSuite.endsWith("_SHA256");
@@ -113,6 +129,8 @@
? new String[] { cipherSuite,
StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION }
: new String[] { cipherSuite });
+
+ // Check that handshake succeeds.
TestSSLEnginePair pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
@Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
@@ -132,6 +150,27 @@
assertSendsCorrectly("This is the server. Hi!".getBytes(),
pair.server, pair.client, needsRecordSplit);
assertFalse(errorExpected);
+
+ // Check that handshake fails when the server does not possess the private key
+ // corresponding to the server's certificate. This is achieved by using SSLContext
+ // cWithWrongPrivateKeys whose KeyManager returns wrong private keys that match
+ // the algorithm (and parameters) of the correct keys.
+ if (!cipherSuite.contains("_anon_")) {
+ // The identity of the server is verified only in non-anonymous key exchanges.
+ try {
+ TestSSLEnginePair p = TestSSLEnginePair.create(
+ cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ client.setEnabledCipherSuites(cipherSuiteArray);
+ server.setEnabledCipherSuites(cipherSuiteArray);
+ }
+ });
+ assertConnected(p);
+ fail("Handshake succeeded for " + cipherSuite
+ + " despite server not having the correct private key");
+ } catch (IOException expected) {}
+ }
} catch (Exception maybeExpected) {
if (!errorExpected) {
throw new Exception("Problem trying to connect cipher suite " + cipherSuite,
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/provider/crypto/SHA1PRNG_SecureRandomTest.java b/luni/src/test/java/org/apache/harmony/security/tests/provider/crypto/SHA1PRNG_SecureRandomTest.java
index ffdbe92..0db5ee1 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/provider/crypto/SHA1PRNG_SecureRandomTest.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/provider/crypto/SHA1PRNG_SecureRandomTest.java
@@ -17,6 +17,8 @@
package org.apache.harmony.security.tests.provider.crypto;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
@@ -400,4 +402,31 @@
assertFalse("sequences are equal i=" + i, b);
}
}
+
+ public void testSeedIsFullLength() throws Exception {
+ Class<?> srClass = Class.forName(
+ "org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl");
+ Field seedField = srClass.getDeclaredField("seed");
+ seedField.setAccessible(true);
+
+ Method nextBytesMethod = srClass.getDeclaredMethod("engineNextBytes", byte[].class);
+ nextBytesMethod.setAccessible(true);
+
+ byte[] bytes = new byte[1];
+
+ // Iterate 8 times to make sure the probability of a false positive is
+ // extremely rare.
+ for (int i = 0; i < 8; i++) {
+ Object sr = srClass.newInstance();
+ nextBytesMethod.invoke(sr, bytes);
+ int[] seed = (int[]) seedField.get(sr);
+
+ // If the first integer is not zero, it is fixed.
+ if (seed[0] != 0) {
+ return; // Success
+ }
+ }
+
+ fail("Fallback SHA1PRNG_SecureRandomImpl should not clobber seed internally");
+ }
}
diff --git a/support/src/test/java/libcore/javax/net/ssl/ForwardingX509ExtendedKeyManager.java b/support/src/test/java/libcore/javax/net/ssl/ForwardingX509ExtendedKeyManager.java
new file mode 100644
index 0000000..e901d34
--- /dev/null
+++ b/support/src/test/java/libcore/javax/net/ssl/ForwardingX509ExtendedKeyManager.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package libcore.javax.net.ssl;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+
+/**
+ * {@link X509ExtendedKeyManager} which delegates all calls to the provided
+ * {@code X509ExtendedKeyManager} instance.
+ */
+public class ForwardingX509ExtendedKeyManager extends X509ExtendedKeyManager {
+ private final X509ExtendedKeyManager delegate;
+
+ public ForwardingX509ExtendedKeyManager(X509ExtendedKeyManager delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
+ return delegate.chooseClientAlias(keyType, issuers, socket);
+ }
+
+ @Override
+ public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+ return delegate.chooseServerAlias(keyType, issuers, socket);
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ return delegate.getCertificateChain(alias);
+ }
+
+ @Override
+ public String[] getClientAliases(String keyType, Principal[] issuers) {
+ return delegate.getClientAliases(keyType, issuers);
+ }
+
+ @Override
+ public String[] getServerAliases(String keyType, Principal[] issuers) {
+ return delegate.getServerAliases(keyType, issuers);
+ }
+
+ @Override
+ public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
+ return delegate.chooseEngineClientAlias(keyType, issuers, engine);
+ }
+
+ @Override
+ public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
+ return delegate.chooseEngineServerAlias(keyType, issuers, engine);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ return delegate.getPrivateKey(alias);
+ }
+}
diff --git a/support/src/test/java/libcore/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java b/support/src/test/java/libcore/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java
new file mode 100644
index 0000000..fd5cc0b
--- /dev/null
+++ b/support/src/test/java/libcore/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package libcore.javax.net.ssl;
+
+import junit.framework.Assert;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+import javax.net.ssl.X509ExtendedKeyManager;
+
+/**
+ * {@link X509ExtendedKeyManager} which forwards all calls to a delegate while substituting
+ * the returned private key with its own randomly generated keys of the same type (and parameters).
+ */
+public class RandomPrivateKeyX509ExtendedKeyManager extends ForwardingX509ExtendedKeyManager {
+
+ private final Map<String, PrivateKey> cachedKeys = new HashMap<String, PrivateKey>();
+
+ public RandomPrivateKeyX509ExtendedKeyManager(X509ExtendedKeyManager delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ PrivateKey originalPrivateKey = super.getPrivateKey(alias);
+ if (originalPrivateKey == null) {
+ return null;
+ }
+
+ PrivateKey result;
+ String keyAlgorithm = originalPrivateKey.getAlgorithm();
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
+ if ("RSA".equals(keyAlgorithm)) {
+ RSAPrivateKeySpec originalKeySpec =
+ keyFactory.getKeySpec(originalPrivateKey, RSAPrivateKeySpec.class);
+ int keyLengthBits = originalKeySpec.getModulus().bitLength();
+ // Use a cache because RSA key generation is slow.
+ String cacheKey = keyAlgorithm + "-" + keyLengthBits;
+ result = cachedKeys.get(cacheKey);
+ if (result == null) {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
+ keyPairGenerator.initialize(keyLengthBits);
+ result = keyPairGenerator.generateKeyPair().getPrivate();
+ cachedKeys.put(cacheKey, result);
+ }
+ } else if ("DSA".equals(keyAlgorithm)) {
+ DSAPrivateKeySpec originalKeySpec =
+ keyFactory.getKeySpec(originalPrivateKey, DSAPrivateKeySpec.class);
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
+ keyPairGenerator.initialize(new DSAParameterSpec(
+ originalKeySpec.getP(), originalKeySpec.getQ(), originalKeySpec.getG()));
+ result = keyPairGenerator.generateKeyPair().getPrivate();
+ } else {
+ Assert.fail("Unsupported key algorithm: " + originalPrivateKey.getAlgorithm());
+ result = null;
+ }
+ } catch (GeneralSecurityException e) {
+ Assert.fail("Failed to generate private key: " + e);
+ result = null;
+ }
+
+ return result;
+ }
+}