Code Monkey home page Code Monkey logo

composeinvestigator's Introduction

I'm interested in...

  • Gradle Plugin
  • Kotlin Compiler Plugin
  • Compose Runtime
  • Kotlin Multiplatform
  • Experience Engineering
  • UX Writing

composeinvestigator's People

Contributors

jisungbin avatar rayworks avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

composeinvestigator's Issues

Propose a new way to determine composable invalidation tracking scope

Right now we're limiting the invalidation tracking scope to the trace range of the composable, but this doesn't seem to be an effective way to do it. Every composable will have its own group unless we arbitrarily remove the composable group. Alternatively, we can intercept the ComposableLambda. This would have a more precise invalidation tracking scope, but each composable would not be named per group. This would need to be resolved first before adopting this approach.

A composable is divided into the composable function itself and the ComposableSingleton. The latter means to composable lambdas, and this proposal can be applied to the latter.

If this proposal is approved, it will probably solve this problem as well.

Feature Request: Composable callstack graph

Background of Feature Request

Composables are developed as reusable functions, allowing a single composable to be used in multiple places. In the given Kotlin code structure:

@Composable fun House() {
  Content()
}

@Composable fun Door() {
  Content()
}

@Composable fun Content() {
  Text("It's me!")
}

When both House and Door composables recompose, the Content composable is also recomposed. However, the Content composable has no knowledge beyond being recomposed; it doesn't know which parent triggered the recomposition. For effective debugging, it is crucial to know which parent caused the recomposition of the Content composable. Currently, developers have to inspect both House and Door composables to identify the source of unintended recomposition.

Proposed Implementation Approach

The idea is to wrap all composable function calls with a try-finally block to record the call stack. For instance:

@Composable fun House() {
  try {
    // Enter `Content()`
    Content()
  } finally {
    // Exit `Content()` 
  }
}

Extending this approach to wrap all composable function calls in a try-finally block and record the call stack could provide valuable debugging information.

Implementation Considerations

Composable lambdas that do not capture values are managed by the ComposableSingleton class, while lambdas that capture values are wrapped in the ComposableLambda class. Therefore, unintentionally, the ComposableSingleton or ComposableLambda classes may be present in the Compose function call stack. To prevent this, recording the call stack should be performed before the Compose compiler.

Assistance Needed

I attempted to implement this quickly in pull request #78, and while the try-finally concept worked, there were issues. Composables can be defined not only as functions but also as anonymous functions. For example:

@Composable fun Content(content: @Composable () -> Unit) {
  content()
}

@Composable fun ActualContent() {
  Content {
    Text("beep bop boop")
  }
}

The expected call stack for Text("beep bop boop") would be ActaualContent -> Content -> Text. However, in reality, an <anonymous> layer is introduced because Text("beep bop boop") is created from an anonymous composable function.

@Composable fun ActualContent() {
  Content(
    content = @Composable {
      Text("beep bop boop")
    }
  )
}

Replacing <anonymous> with the parameter name content provides a meaningful name, but is this approach always valid?

For now, I'm creating this issue to document my current situation and will continue to develop this way.

App crashes when tracking DerivedState

Reproduce Step : Just launch app with this plugin applied.
composeBomVersion = "2023.10.01"
composeCompiler = "1.5.10"

Stacktrace:

java.lang.RuntimeException: Unable to resume activity {com.gangnam.sister.debug/com.kotlin.activity.MainActivity}: java.lang.ClassCastException: androidx.compose.runtime.DerivedSnapshotState cannot be cast to androidx.compose.runtime.snapshots.SnapshotMutableState at android.app.ActivityThread.performResumeActivity(ActivityThread.java:5426) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:5507) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:57) at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:180) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:98) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2685) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:230) at android.os.Looper.loop(Looper.java:319) at android.app.ActivityThread.main(ActivityThread.java:8913) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103) Caused by: java.lang.ClassCastException: androidx.compose.runtime.DerivedSnapshotState cannot be cast to androidx.compose.runtime.snapshots.SnapshotMutableState at land.sungbin.composeinvestigator.runtime.ComposeStateObjectValueGetter.getCurrentValue(StateObjectTracker.kt:234) at land.sungbin.composeinvestigator.runtime.ComposeStateObjectValueGetter.initialize$runtime_release(StateObjectTracker.kt:240) at land.sungbin.composeinvestigator.runtime.StateObjectTrackerKt$registerStateObjectTracking$1$register$2$1.onRemembered(StateObjectTracker.kt:159) at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1295) at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:984) at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1005) at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1099) at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:633) at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:619) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:123) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:114) at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1289) at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:114) at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:164) at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.kt:314) at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.kt:251) at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.kt:287) at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.kt:136) at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.kt:119) at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:100) at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:708) at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:3176) at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:3161) at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:619) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:275) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1943) 2024-03-05 13:21:40.287 26853-26853 AndroidRuntime com.gangnam.sister.debug E at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1839) at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1751) at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:323) at androidx.fragment.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:249) at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1244) at androidx.viewpager.widget.ViewPager.populate(ViewPager.java:1092) at androidx.viewpager.widget.ViewPager.setAdapter(ViewPager.java:540) at com.kotlin.activity.MainActivity.initAdapter(MainActivity.kt:601) at com.kotlin.presenter.MainPresenter.onResume(MainPresenter.kt:78) at com.kotlin.activity.MainActivity.onResume(MainActivity.kt:561) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1603) at android.app.Activity.performResume(Activity.java:9119) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:5399) ... 13 more

Logger initialization timing issue

There is a bug where the logger initialization is not synchronous. We probably need to reimplement the logger initialization in a different way to fix this.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • actions/setup-java v4
  • android-actions/setup-android v3
  • gradle/gradle-build-action v3
  • actions/upload-artifact v4
  • actions/upload-artifact v4
.github/workflows/document-deploy.yml
  • actions/checkout v4
  • actions/setup-java v4
  • android-actions/setup-android v3
  • gradle/gradle-build-action v3
  • actions/setup-python v5
  • actions/configure-pages v4
  • actions/upload-pages-artifact v3
  • actions/deploy-pages v4
.github/workflows/gradle-wrapper-validation.yml
  • actions/checkout v4
  • gradle/wrapper-validation-action v2
gradle
gradle.properties
settings.gradle.kts
build.gradle.kts
compiler/gradle.properties
compiler/build.gradle.kts
compiler-gradle-plugin/gradle.properties
compiler-gradle-plugin/build.gradle.kts
compiler-hosted/gradle.properties
compiler-hosted/build.gradle.kts
compiler-integration-test/build.gradle.kts
gradle/libs.versions.toml
  • com.android.tools.build:gradle 8.2.2
  • org.jetbrains:annotations 24.1.0
  • org.jetbrains.kotlin:kotlin-gradle-plugin 1.9.23
  • org.jetbrains.kotlin:kotlin-gradle-plugin-api 1.9.23
  • org.jetbrains.kotlin:kotlin-compiler 1.9.23
  • androidx.activity:activity-ktx 1.8.2
  • androidx.compose.compiler:compiler-hosted 1.5.11
  • androidx.compose.runtime:runtime 1.6.4
  • androidx.compose.animation:animation-core 1.6.4
  • androidx.compose.material:material 1.6.4
  • androidx.activity:activity-compose 1.8.2
  • land.sungbin.fastlist:fastlist 0.1.0
  • io.mockk:mockk 1.13.10
  • io.kotest:kotest-runner-junit5 5.8.1
  • io.kotest:kotest-assertions-core 5.8.1
  • junit:junit 4.13.2
  • org.junit.vintage:junit-vintage-engine 5.10.2
  • androidx.compose.ui:ui-test-junit4 1.6.4
  • org.robolectric:robolectric 4.11.1
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.8.0
  • dev.zacsweers.kctfork:core 0.4.0
  • com.pinterest.ktlint:ktlint-cli 1.1.1
  • com.vanniktech.maven.publish 0.28.0
  • com.adarshr.test-logger 4.0.0
  • com.github.johnrengelman.shadow 8.1.1
  • org.jetbrains.dokka 1.9.20
  • com.diffplug.spotless 6.25.0
runtime/gradle.properties
runtime/build.gradle.kts
sample/build.gradle.kts
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.6
pip_requirements
documentation/requirements.txt
  • jinja2 ~=3.0
  • markdown ~=3.2
  • mkdocs ~=1.5.3
  • mkdocs-material-extensions ~=1.3
  • pygments ~=2.16
  • pymdown-extensions ~=10.2
  • babel ~=2.10
  • colorama ~=0.4
  • paginate ~=0.5
  • regex >=2022.4
  • requests ~=2.26

  • Check this box to trigger a request for Renovate to run again on this repository

App crashed when retrieving the animationState from Animatable object via reflection

E  Uncaught Exception on Thread[main,5,main], on device: OnePlus/OnePlus7Pro_CH/OnePlus7Pro:12/SKQ1.211113.001/P.202303230244:user/release-keys
    java.lang.IllegalArgumentException: Expected receiver of type androidx.compose.animation.core.Animatable, but got land.sungbin.composeinvestigator.runtime.ComposeStateObjectGetter
    	at java.lang.reflect.Field.get(Native Method)
    	at land.sungbin.composeinvestigator.runtime.ComposeStateObjectGetter.invoke(StateObjectTracker.kt:205)
    	at land.sungbin.composeinvestigator.runtime.StateObjectTrackerKt$registerStateObjectTracking$1$register$2$1.onRemembered(StateObjectTracker.kt:154)
    	at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1295)
    	at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:984)
    	at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1005)
    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:639)
    	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:551)
    	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:41)
    	at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
    	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
    	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
    	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1230)
    	at android.view.Choreographer.doCallbacks(Choreographer.java:1029)
    	at android.view.Choreographer.doFrame(Choreographer.java:925)
    	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1217)
    	at android.os.Handler.handleCallback(Handler.java:938)
    	at android.os.Handler.dispatchMessage(Handler.java:99)
    	at android.os.Looper.loopOnce(Looper.java:233)
    	at android.os.Looper.loop(Looper.java:344)
    	at android.app.ActivityThread.main(ActivityThread.java:8212)
    	at java.lang.reflect.Method.invoke(Native Method)
    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034)
    	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@e3b99af, androidx.compose.ui.platform.MotionDurationScaleImpl@48df1bc, StandaloneCoroutine{Cancelling}@37f6545, AndroidUiDispatcher@677549a]

Support for tracking property changes

Currently, only the arguments of composable functions support change tracking. It is common too to have a State property inside a composable function and to change that state directly to request a recomposition without state hoisting.

image

new name suggestion

I propose "ComposeInvestigator" as a new name, as it looks like the main feature is going to be an invalidation tracker.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.