Skip to content
This repository has been archived by the owner on Nov 22, 2024. It is now read-only.

Commit

Permalink
Add Tree Configuration into UI Debugger
Browse files Browse the repository at this point in the history
Summary: This diff adds a Tree Configuration into the UI Debugger for Litho components. The changes made in the code include adding a new descriptor for the ComponentTree and registering it with the DescriptorRegister. The MetadataRegister is also updated to include the Tree Configuration metadata.

Reviewed By: LukeDefeo

Differential Revision: D52206307

fbshipit-source-id: 1f2abb79d46c2361187192eb7de06795060f37a9
  • Loading branch information
Fabio Carballo authored and facebook-github-bot committed Dec 18, 2023
1 parent 7e11573 commit 1323d3b
Showing 1 changed file with 89 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,37 @@ package com.facebook.flipper.plugins.uidebugger.litho.descriptors

import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
import com.facebook.flipper.plugins.uidebugger.descriptors.OffsetChild
import com.facebook.flipper.plugins.uidebugger.litho.LithoTag
import com.facebook.flipper.plugins.uidebugger.model.Bounds
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
import com.facebook.flipper.plugins.uidebugger.util.Immediate
import com.facebook.flipper.plugins.uidebugger.util.Deferred
import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
import com.facebook.litho.ComponentTree
import com.facebook.litho.DebugComponent
import com.facebook.litho.config.ComponentsConfiguration
import java.lang.Exception
import java.lang.reflect.Field
import java.lang.reflect.Modifier

class ComponentTreeDescriptor(val register: DescriptorRegister) : NodeDescriptor<ComponentTree> {

private val NAMESPACE = "ComponentTree"

private val MetadataSectionId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Tree Metadata")

private val ConfigurationSectionId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Tree Configuration")

private val ComponentTreeId =
MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, "treeConfiguration", "id")

private val qualifiedName = ComponentTree::class.qualifiedName ?: ""

override fun getId(node: ComponentTree): Id = node.id
Expand Down Expand Up @@ -56,8 +74,77 @@ class ComponentTreeDescriptor(val register: DescriptorRegister) : NodeDescriptor
override fun getAttributes(
node: ComponentTree
): MaybeDeferred<Map<MetadataId, InspectableObject>> {
return Immediate(mapOf())
return Deferred {
val attributeSections = mutableMapOf<MetadataId, InspectableObject>()

attributeSections[MetadataSectionId] =
InspectableObject(mapOf(ComponentTreeId to InspectableValue.Number(node.id)))

attributeSections[ConfigurationSectionId] =
InspectableObject(getLithoConfigurationAttributes(node))

attributeSections
}
}

private fun getLithoConfigurationAttributes(tree: ComponentTree): Map<MetadataId, Inspectable> {
val (configurationInstance, configurationFields) =
getConfigurationFieldsAndValuesUsingReflection(tree)

return configurationFields
.filter { !Modifier.isStatic(it.modifiers) }
.associate { field ->
val metadataId =
MetadataRegister.register(
MetadataRegister.TYPE_ATTRIBUTE, "componentsConfiguration", field.name)

field.isAccessible = true

val inspectableValue =
when (val value = field.get(configurationInstance)) {
is Number -> InspectableValue.Number(value)
is Boolean -> InspectableValue.Boolean(value)
is String -> InspectableValue.Text(value)
else -> InspectableValue.Unknown(value?.toString() ?: "null")
}

metadataId to inspectableValue
}
}

/**
* This function is only being used while Litho doesn't release a new OSS version. This is needed
* because both the [ComponentsConfiguration] and [ComponentTree.getLithoConfiguration] have been
* modified since the version Flipper OSS has been associated with.
*/
private fun getConfigurationFieldsAndValuesUsingReflection(
tree: ComponentTree
): Pair<Any?, Array<Field>> {
try {
// TODO: use `getConfigurationFieldsAndValues` once Litho does a new OSS release
// 1. get LithoConfiguration
val lithoConfigurationMethod = tree.javaClass.getDeclaredMethod("getLithoConfiguration")
val lithoConfigurationInstance = lithoConfigurationMethod.invoke(tree)

// 2. get ComponentsConfiguration from LithoConfiguration instance
val configurationField =
lithoConfigurationInstance?.javaClass?.getDeclaredField("componentsConfig")
val configurationInstance = configurationField?.get(lithoConfigurationInstance)

// 3. get the fields from the configuration
val configurationFields = configurationInstance?.javaClass?.declaredFields ?: emptyArray()
return configurationInstance to configurationFields
} catch (exception: Exception) {
// Getting all exceptions since this is so britle and we don't want to crash the whole UI
// Debugger.
return null to emptyArray()
}
}

// private fun getConfigurationFieldsAndValues(tree: ComponentTree): Pair<Any?, Array<Field>> {
// val configuration = tree.lithoConfiguration.componentsConfig
// return configuration to configuration.javaClass.declaredFields
// }

override fun getTags(node: ComponentTree): Set<String> = setOf(LithoTag, "TreeRoot")
}

0 comments on commit 1323d3b

Please sign in to comment.