From 20a35f6ceb3be60a94a72c783e6dda0d462e2c7e Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Tue, 9 Jun 2026 03:19:33 -0700 Subject: [PATCH] Fix RuntimeException in SkewMatrixHelper when transform map is empty Summary: Fixes a crash (`java.lang.RuntimeException` via `NoSuchElementException`) in `SkewMatrixHelper.isAffine2DTransformWithSkew` and `buildAffine2DMatrix` when a transform array contains an empty map entry (no keys). Changelog: [Android][Fixed] Fix crash in SkewMatrixHelper Differential Revision: D107763077 --- .../react/uimanager/SkewMatrixHelper.kt | 8 +++++-- .../react/uimanager/SkewMatrixHelperTest.kt | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt index 5770c4cbc6bb..315afff089a4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt @@ -36,7 +36,9 @@ internal object SkewMatrixHelper { for (i in 0 until transforms.size()) { if (transforms.getType(i) != ReadableType.Map) continue val map = transforms.getMap(i) ?: continue - val type = map.keySetIterator().nextKey() + val iterator = map.keySetIterator() + if (!iterator.hasNextKey()) continue + val type = iterator.nextKey() when (type) { "matrix", "perspective", @@ -98,7 +100,9 @@ internal object SkewMatrixHelper { for (i in 0 until transforms.size()) { if (transforms.getType(i) != ReadableType.Map) continue val map = transforms.getMap(i) ?: continue - val type = map.keySetIterator().nextKey() + val iterator = map.keySetIterator() + if (!iterator.hasNextKey()) continue + val type = iterator.nextKey() when (type) { "rotate", "rotateZ" -> diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/SkewMatrixHelperTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/SkewMatrixHelperTest.kt index 3ad01dcc41b3..bb0c2a0e9bc2 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/SkewMatrixHelperTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/SkewMatrixHelperTest.kt @@ -180,6 +180,27 @@ class SkewMatrixHelperTest { assertThat(values[Matrix.MTRANS_X]).isCloseTo(0f, within(EPSILON)) } + @Test + fun emptyMapInTransforms_doesNotThrow() { + // Regression test for T274692242: an empty map in the transforms array caused + // NoSuchElementException from keySetIterator().nextKey() without hasNextKey() guard. + val transforms = + JavaOnlyArray.of( + JavaOnlyMap(), // empty map — previously crashed + JavaOnlyMap.of("skewX", "20deg"), + ) + + // isAffine2DTransformWithSkew must not throw and should still detect skew + assertThat(SkewMatrixHelper.isAffine2DTransformWithSkew(transforms)).isTrue() + + // buildAffine2DMatrix must not throw when encountering the empty map + val matrix = SkewMatrixHelper.buildAffine2DMatrix(transforms, 100f, 100f, null) + val values = matrixValues(matrix) + // The empty map is skipped; only the skewX entry contributes + val tan20 = Math.tan(Math.toRadians(20.0)).toFloat() + assertThat(values[Matrix.MSKEW_X]).isCloseTo(tan20, within(EPSILON)) + } + private fun matrixValues(matrix: Matrix): FloatArray { val values = FloatArray(9) matrix.getValues(values)