Skip to content

Commit

Permalink
Fix crash in state restoration with multiple of 32 fields (#707)
Browse files Browse the repository at this point in the history
* If the field cound was exactly multiples of 32 the bitmap count would be be too high (e.g. 2 with field count 32 and 3 with field count 64) leading to a crash when calling the copyt function via reflection.

Added test that tests that case.

* Use ceil to calculate bitmap size.

---------

Co-authored-by: Andreas Rossbacher <andreas.rossbacher@airbnb.com>
  • Loading branch information
rossbacher and Andreas Rossbacher authored Jan 25, 2024
1 parent 376ca57 commit 68e4869
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
3 changes: 2 additions & 1 deletion mvrx/src/main/kotlin/com/airbnb/mvrx/PersistState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.os.Parcelable
import java.io.Serializable
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import kotlin.math.ceil

/**
* Annotate a field in your [MavericksViewModel] state with [PersistState] to have it automatically persisted when Android kills your process
Expand Down Expand Up @@ -128,7 +129,7 @@ fun <T : MavericksState> restorePersistedMavericksState(
val fieldCount = constructor.parameterTypes.size

// There is 1 bitmask for each block of 32 parameters.
val parameterBitMasks = IntArray(fieldCount / 32 + 1) { 0 }
val parameterBitMasks = IntArray(ceil(fieldCount / 32.0).toInt())
val parameters = arrayOfNulls<Any?>(fieldCount)
parameters[0] = initialState
for (i in 0 until fieldCount) {
Expand Down
52 changes: 52 additions & 0 deletions mvrx/src/test/kotlin/com/airbnb/mvrx/PersistedStateTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,58 @@ class PersistedStateTest : BaseTest() {
persistMavericksState(State())
}

@Test
fun testClassWithExactly32Parameters() {
data class StateWith32Params(
val p0: Int = 0,
@PersistState val p1: Int = 0,
val p2: Int = 0,
@PersistState val p3: Int = 0,
val p4: Int = 0,
@PersistState val p5: Int = 0,
val p6: Int = 0,
@PersistState val p7: Int = 0,
val p8: Int = 0,
@PersistState val p9: Int = 0,
val p10: Int = 0,
@PersistState val p11: Int = 0,
val p12: Int = 0,
@PersistState val p13: Int = 0,
val p14: Int = 0,
@PersistState val p15: Int = 0,
val p16: Int = 0,
@PersistState val p17: Int = 0,
val p18: Int = 0,
@PersistState val p19: Int = 0,
val p20: Int = 0,
@PersistState val p21: Int = 0,
val p22: Int = 0,
@PersistState val p23: Int = 0,
val p24: Int = 0,
@PersistState val p25: Int = 0,
val p26: Int = 0,
@PersistState val p27: Int = 0,
val p28: Int = 0,
@PersistState val p29: Int = 0,
val p30: Int = 0,
@PersistState val p31: Int = 0,
) : MavericksState

val bundle = persistMavericksState(
StateWith32Params(
p0 = 1,
p1 = 2,
p30 = 0,
p31 = 17,
)
)
val newState = restorePersistedMavericksState(bundle, StateWith32Params())
assertEquals(2, newState.p1)
assertEquals(17, newState.p31)
assertEquals(0, newState.p30)
assertEquals(17, newState.p31)
}

@Test
fun testClassWithMoreThan32Parameters() {
data class StateWithLotsOfParameters(
Expand Down

0 comments on commit 68e4869

Please sign in to comment.