package shark.internal import shark.internal.ChainingInstanceReferenceReader.VirtualInstanceReferenceReader.OptionalFactory import shark.internal.ChainingInstanceReferenceReader.VirtualInstanceReferenceReader import shark.HeapGraph import shark.HeapObject.HeapInstance /** * Defines [VirtualInstanceReferenceReader] factories for common Apache Harmony data structures. * * Note: the expanders target the direct classes and don't target subclasses, as these might * include additional out going references that would be missed. */ internal enum class ApacheHarmonyInstanceRefReaders : OptionalFactory { // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/LinkedList.java LINKED_LIST { override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { val linkedListClass = graph.findClassByName("java.util.LinkedList") ?: return null val isApacheHarmonyImpl = linkedListClass.readRecordFields() .any { linkedListClass.instanceFieldName(it) == "voidLink" } if (!isApacheHarmonyImpl) { return null } return InternalSharedLinkedListReferenceReader( classObjectId = linkedListClass.objectId, headFieldName = "voidLink", nodeClassName = "java.util.LinkedList\$Link", nodeNextFieldName = "next", nodeElementFieldName = "data", ) } }, // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/ArrayList.java ARRAY_LIST { override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { val arrayListClass = graph.findClassByName("java.util.ArrayList") ?: return null val isApacheHarmonyImpl = arrayListClass.readRecordFields() .any { arrayListClass.instanceFieldName(it) == "array" } if (!isApacheHarmonyImpl) { return null } return InternalSharedArrayListReferenceReader( className = "java.util.ArrayList", classObjectId = arrayListClass.objectId, elementArrayName = "array", sizeFieldName = "size", ) } }, // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java COPY_ON_WRITE_ARRAY_LIST { override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { val arrayListClass = graph.findClassByName("java.util.concurrent.CopyOnWriteArrayList") ?: return null val isApacheHarmonyImpl = arrayListClass.readRecordFields() .any { arrayListClass.instanceFieldName(it) == "elements" } if (!isApacheHarmonyImpl) { return null } return InternalSharedArrayListReferenceReader( className = "java.util.concurrent.CopyOnWriteArrayList", classObjectId = arrayListClass.objectId, elementArrayName = "elements", sizeFieldName = null, ) } }, // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/HashMap.java /** * Handles HashMap & LinkedHashMap */ HASH_MAP { override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { val hashMapClass = graph.findClassByName("java.util.HashMap") ?: return null // No loadFactor field in the Apache Harmony impl. val isOpenJdkImpl = hashMapClass.readRecordFields() .any { hashMapClass.instanceFieldName(it) == "loadFactor" } if (isOpenJdkImpl) { return null } val linkedHashMapClass = graph.findClassByName("java.util.LinkedHashMap") val hashMapClassId = hashMapClass.objectId val linkedHashMapClassId = linkedHashMapClass?.objectId ?: 0 return InternalSharedHashMapReferenceReader( className = "java.util.HashMap", tableFieldName = "table", nodeClassName = "java.util.HashMap\$HashMapEntry", nodeNextFieldName = "next", nodeKeyFieldName = "key", nodeValueFieldName = "value", keyName = "key()", keysOnly = false, matches = { val instanceClassId = it.instanceClassId instanceClassId == hashMapClassId || instanceClassId == linkedHashMapClassId }, declaringClassId = { it.instanceClassId } ) } }, // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/HashSet.java /** * Handles HashSet & LinkedHashSet */ HASH_SET { override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { val hashSetClass = graph.findClassByName("java.util.HashSet") ?: return null val isApacheHarmonyImpl = hashSetClass.readRecordFields() .any { hashSetClass.instanceFieldName(it) == "backingMap" } if (!isApacheHarmonyImpl) { return null } val linkedHashSetClass = graph.findClassByName("java.util.LinkedHashSet") val hashSetClassId = hashSetClass.objectId val linkedHashSetClassId = linkedHashSetClass?.objectId ?: 0 return object : VirtualInstanceReferenceReader { override fun matches(instance: HeapInstance): Boolean { val instanceClassId = instance.instanceClassId return (instanceClassId == hashSetClassId || instanceClassId == linkedHashSetClassId) } override fun read(instance: HeapInstance): Sequence { // "HashSet.backingMap" is never null. val map = instance["java.util.HashSet", "backingMap"]!!.valueAsInstance!! return InternalSharedHashMapReferenceReader( className = "java.util.HashMap", tableFieldName = "table", nodeClassName = "java.util.HashMap\$HashMapEntry", nodeNextFieldName = "next", nodeKeyFieldName = "key", nodeValueFieldName = "value", keyName = "element()", keysOnly = true, matches = { true }, declaringClassId = { instance.instanceClassId } ).read(map) } } } } ; }