diff options
50 files changed, 12459 insertions, 455 deletions
diff --git a/JavaLibrary.mk b/JavaLibrary.mk index 5dc939e..c44d991 100644 --- a/JavaLibrary.mk +++ b/JavaLibrary.mk @@ -50,18 +50,20 @@ $(shell cd $(LOCAL_PATH) && ls -d */src/$(1)/{java,resources} 2> /dev/null) 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 @@ local_javac_flags+=-Xmaxwarns 9999999 # 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 @@ include $(BUILD_HOST_JAVA_LIBRARY) 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 caaf9c4..ff0be41 100644 --- a/dalvik/src/main/java/dalvik/system/DexPathList.java +++ b/dalvik/src/main/java/dalvik/system/DexPathList.java @@ -150,17 +150,13 @@ import static libcore.io.OsConstants.*; * 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 @@ public final class VMDebug { /** * 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 @@ public final class VMDebug { /** * 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 @@ public final class VMDebug { * @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 @@ public final class Dex { 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 final class Dex { } 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 @@ public final class Dex { 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 final class Dex { } 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 final class Dex { 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 @@ public final class Dex { Try[] tries; CatchHandler[] catchHandlers; if (triesSize > 0) { - 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); + 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); } 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 final class Dex { } 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 @@ public final class Dex { 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 final class DexFormat { 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 @@ import com.android.dex.util.Unsigned; 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..19e18da --- /dev/null +++ b/libart/src/main/java/dalvik/system/VMRuntime.java @@ -0,0 +1,243 @@ +/* + * 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(); +} 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 71098be..cd4a9db 100644 --- a/libdvm/src/main/java/dalvik/system/VMRuntime.java +++ b/libdvm/src/main/java/dalvik/system/VMRuntime.java @@ -68,6 +68,11 @@ public final class VMRuntime { 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 @@ -117,7 +122,7 @@ public final class VMRuntime { /** * 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() { @@ -126,7 +131,7 @@ public final class VMRuntime { /** * 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) { @@ -159,7 +164,7 @@ public final class VMRuntime { /** * 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) { @@ -168,14 +173,14 @@ public final class VMRuntime { /** * 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() { @@ -217,4 +222,19 @@ public final class VMRuntime { * 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); } 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.Arrays; 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 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe private static final long serialVersionUID = 3206093459760846163L; /** - * Lazily computed name of this class; always prefer calling getName(). + * 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 String name; + private transient int dexClassDefIndex; + /** The type index of this class within the dex file that defines it. */ private transient int dexTypeIndex; + /** + * Have we computed the type and class def indices? Volatile to avoid double check locking bugs. + */ + private transient volatile boolean dexIndicesInitialized; + + /** + * Lazily computed name of this class; always prefer calling getName(). + */ + private transient String name; + private Class() { // Prevent this class to be instantiated, instance // should be created by JVM only @@ -139,44 +152,48 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe */ 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 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe 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 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * 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 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe */ @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 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * {@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 final class Class<T> implements Serializable, AnnotatedElement, GenericDe public String toString() { if (isPrimitive()) { return getSimpleName(); - } else { - return (isInterface() ? "interface " : "class ") + getName(); } + return (isInterface() ? "interface " : "class ") + getName(); } /** @@ -1267,13 +1301,48 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * * @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 531f269..ab417d2 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 @@ public final class Constructor<T> extends AccessibleObject implements GenericDec 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 final class Constructor<T> extends AccessibleObject implements GenericDec */ public Type[] getGenericParameterTypes() { initGenericTypes(); - return Types.getClonedTypeArray(genericParameterTypes); + return Types.getTypeArray(genericParameterTypes, true); } /** @@ -203,7 +201,7 @@ public final class Constructor<T> extends AccessibleObject implements GenericDec */ 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 @@ public final class Method extends AccessibleObject implements GenericDeclaration 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 final class Method extends AccessibleObject implements GenericDeclaration */ public Type[] getGenericParameterTypes() { initGenericTypes(); - return Types.getClonedTypeArray(genericParameterTypes); + return Types.getTypeArray(genericParameterTypes, true); } /** @@ -234,7 +232,7 @@ public final class Method extends AccessibleObject implements GenericDeclaration */ 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 class Modifier { 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 @@ public class Modifier { } /** + * 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 @@ import java.util.Arrays; * 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 @@ import libcore.icu.TimeZoneNames; * <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 @@ import libcore.icu.TimeZoneNames; * 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 @@ import libcore.icu.ICU; * <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.IOException; 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 @@ public class ZipEntry implements ZipConstants, Cloneable { /* * 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 @@ public class ZipEntry implements ZipConstants, Cloneable { 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 @@ package java.util.zip; 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 @@ import libcore.io.Streams; * 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 @@ public class ZipFile implements Closeable, ZipConstants { 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 @@ public class ZipFile implements Closeable, ZipConstants { 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 @@ public class ZipFile implements Closeable, ZipConstants { 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 @@ public class ZipFile implements Closeable, ZipConstants { 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 @@ public class ZipFile implements Closeable, ZipConstants { } } + 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 @@ public class ZipFile implements Closeable, ZipConstants { */ 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 @@ public class ZipFile implements Closeable, ZipConstants { @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 @@ public class ZipFile implements Closeable, ZipConstants { } @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 class ZipFile implements Closeable, ZipConstants { 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 @@ public class ZipFile implements Closeable, ZipConstants { } @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/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 @@ public final class AnnotationAccess { 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 @@ public final class AnnotationAccess { */ 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 @@ public final class AnnotationAccess { 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 @@ public final class AnnotationAccess { 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 @@ public final class AnnotationAccess { if (reader == null) { return null; } - return indexToType(c, dex, reader.readType()); + return c.getDexCacheType(dex, reader.readType()); } public static AccessibleObject getEnclosingMethodOrConstructor(Class<?> c) { @@ -529,65 +537,51 @@ public final class AnnotationAccess { * 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) { + private static EncodedValueReader getAnnotationReader( + Dex dex, AnnotatedElement element, String annotationName, int expectedFieldCount) { + int annotationSetOffset = getAnnotationSetOffset(element); if (annotationSetOffset == 0) { - return null; // no annotation + return null; // no annotations on the class } 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(); - if (candidate.getTypeIndex() == annotationType) { - return candidate; + String candidateAnnotationName = dex.typeNames().get(candidate.getTypeIndex()); + if (annotationName.equals(candidateAnnotationName)) { + annotation = candidate; + break; } } - - return null; // this set doesn't carry the annotation - } - - private static EncodedValueReader getAnnotationReader( - Dex dex, AnnotatedElement element, String annotationName, int expectedFieldCount) { - int annotationSetOffset = getAnnotationSetOffset(element); - if (annotationSetOffset == 0) { - return null; // no annotations on the class - } - - int annotationTypeIndex = Collections.binarySearch(dex.typeNames(), annotationName); - com.android.dex.Annotation annotation = getAnnotationFromAnnotationSet( - dex, annotationSetOffset, annotationTypeIndex); 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 @@ public final class AnnotationAccess { 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 @@ public final class AnnotationAccess { } } - 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()); + 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] = context.getDexCacheType(dex, types[i]); + } try { return name.equals("<init>") - ? declaringClass.getDeclaredConstructor(parametersArray) - : declaringClass.getDeclaredMethod(name, parametersArray); + ? 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<?>[] parametersArray = new Class[types.length]; - for (int i = 0; i < types.length; i++) { - parametersArray[i] = indexToType(context, 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; - } - 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; - } + + "." + name + Arrays.toString(parametersArray)); } - throw new AssertionError(); } private static List<Annotation> annotationSetToAnnotations(Class<?> context, int offset) { @@ -722,7 +674,7 @@ public final class AnnotationAccess { 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 @@ public final class AnnotationAccess { 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.GenericSignatureFormatError; 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 @@ public final class GenericSignatureParser { * @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 @@ public final class GenericSignatureParser { 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 @@ public final class GenericSignatureParser { 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 @@ public final class GenericSignatureParser { } } - boolean isStopSymbol(char ch) { + static boolean isStopSymbol(char ch) { switch (ch) { case ':': case '/': @@ -472,7 +497,7 @@ public final class GenericSignatureParser { 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 @@ package libcore.reflect; 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 final class ListOfTypes { 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.lang.reflect.TypeVariable; 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.Type; 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 @@ public final class Types { 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 final class EmptyArray { 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 @@ public class SHA1PRNG_SecureRandomImpl extends SecureRandomSpi implements Serial 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.ZipFile; 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 @@ public final class ZipFileTest extends TestCase { } } + /** + * 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 @@ public final class ZipFileTest extends TestCase { 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")); + // 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 { - out.putNextEntry(ze); - fail(); // The RI fails this test; it just checks the character count at construction time. - } catch (IllegalArgumentException expected) { + ZipFile zipFile = new ZipFile(file); + fail(); + } catch (ZipException expected) { } - out.closeEntry(); - out.putNextEntry(new ZipEntry("okay")); // ZipOutputStream.close throws if you add nothing! - out.close(); } public void testCrc() throws IOException { 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 @@ public class SHA1PRNG_SecureRandomTest extends TestCase { 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"); + } } |