Skip to content

Commit a3820d4

Browse files
dnpetrovSpace
authored and
Space
committed
JVM KT-49548 progression iterators can be tainted
1 parent 63044b1 commit a3820d4

File tree

15 files changed

+191
-32
lines changed

15 files changed

+191
-32
lines changed

compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxedBasicValue.kt

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.resolve.isInlineClass
2424
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
2525
import org.jetbrains.org.objectweb.asm.Type
2626
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
27-
import java.util.*
2827

2928
abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) {
3029
abstract val descriptor: BoxedValueDescriptor

compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt

+29-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
2222
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
2323
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
2424
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
25+
import org.jetbrains.kotlin.codegen.range.*
2526
import org.jetbrains.kotlin.codegen.state.GenerationState
2627
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
2728
import org.jetbrains.kotlin.codegen.topLevelClassInternalName
@@ -35,13 +36,13 @@ import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
3536
import org.jetbrains.org.objectweb.asm.tree.InsnList
3637
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
3738
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
38-
import java.util.*
3939

4040
open class BoxingInterpreter(
4141
private val insnList: InsnList,
4242
private val generationState: GenerationState
4343
) : OptimizationBasicInterpreter() {
4444
private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
45+
private val progressionIterators = HashMap<AbstractInsnNode, ProgressionIteratorBasicValue>()
4546

4647
protected open fun createNewBoxing(
4748
insn: AbstractInsnNode,
@@ -89,8 +90,18 @@ open class BoxingInterpreter(
8990
onUnboxing(insn, firstArg, value.type)
9091
value
9192
}
92-
insn.isIteratorMethodCallOfProgression(values) ->
93-
ProgressionIteratorBasicValue.byProgressionClassType(firstArg.type)
93+
insn.isIteratorMethodCall() -> {
94+
values.markBoxedArgumentValues()
95+
val firstArgType = firstArg.type
96+
if (isProgressionClass(firstArgType)) {
97+
progressionIterators.getOrPut(insn) {
98+
ProgressionIteratorBasicValue.byProgressionClassType(insn, firstArgType)!!
99+
}
100+
} else {
101+
progressionIterators[insn]?.taint()
102+
value
103+
}
104+
}
94105
insn.isNextMethodCallOfProgressionIterator(values) -> {
95106
val progressionIterator = firstArg as? ProgressionIteratorBasicValue
96107
?: throw AssertionError("firstArg should be progression iterator")
@@ -269,14 +280,27 @@ fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicVal
269280
name == "next"
270281
}
271282

283+
fun AbstractInsnNode.isIteratorMethodCall() =
284+
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
285+
name == "iterator" && desc == "()Ljava/util/Iterator;"
286+
}
287+
272288
fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) =
273289
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
274290
val firstArgType = values.firstOrNull()?.type
275-
name == "iterator" && firstArgType != null && isProgressionClass(firstArgType)
291+
name == "iterator" && desc == "()Ljava/util/Iterator;" &&
292+
firstArgType != null && isProgressionClass(firstArgType)
276293
}
277294

295+
private val PROGRESSION_CLASS_FQNS = setOf(
296+
CHAR_RANGE_FQN, CHAR_PROGRESSION_FQN,
297+
INT_RANGE_FQN, INT_PROGRESSION_FQN,
298+
LONG_RANGE_FQN, LONG_PROGRESSION_FQN
299+
)
300+
278301
private fun isProgressionClass(type: Type) =
279-
ProgressionIteratorBasicValue.byProgressionClassType(type) != null
302+
type.className in PROGRESSION_CLASS_FQNS
303+
280304

281305
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
282306
isAreEqualIntrinsic() && areSameTypedPrimitiveBoxedValues(values)

compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/ProgressionIteratorBasicValue.kt

+31-22
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,23 @@ import org.jetbrains.kotlin.codegen.AsmUtil
2020
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
2121
import org.jetbrains.kotlin.codegen.range.*
2222
import org.jetbrains.org.objectweb.asm.Type
23+
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
2324

2425
class ProgressionIteratorBasicValue
2526
private constructor(
27+
val iteratorCallInsn: AbstractInsnNode,
2628
val nextMethodName: String,
2729
iteratorType: Type,
2830
private val primitiveElementType: Type,
2931
val boxedElementType: Type
3032
) : StrictBasicValue(iteratorType) {
3133

32-
private constructor(typeName: String, valuesPrimitiveType: Type, valuesBoxedType: Type = AsmUtil.boxType(valuesPrimitiveType)) :
33-
this("next$typeName", Type.getObjectType("kotlin/collections/${typeName}Iterator"), valuesPrimitiveType, valuesBoxedType)
34+
var tainted = false
35+
private set
36+
37+
fun taint() {
38+
tainted = true
39+
}
3440

3541
val nextMethodDesc: String
3642
get() = "()" + primitiveElementType.descriptor
@@ -47,32 +53,35 @@ private constructor(
4753
super.hashCode() * 31 + nextMethodName.hashCode()
4854

4955
companion object {
50-
private val CHAR_PROGRESSION_ITERATOR_VALUE = ProgressionIteratorBasicValue("Char", Type.CHAR_TYPE)
51-
private val INT_PROGRESSION_ITERATOR_VALUE = ProgressionIteratorBasicValue("Int", Type.INT_TYPE)
52-
private val LONG_PROGRESSION_ITERATOR_VALUE = ProgressionIteratorBasicValue("Long", Type.LONG_TYPE)
53-
5456
// TODO functions returning inline classes are mangled now, should figure out how to work with UInt/ULong iterators here
55-
// private val UINT_PROGRESSION_ITERATOR_VALUE =
5657
// ProgressionIteratorBasicValue("UInt", Type.INT_TYPE, Type.getObjectType("kotlin/UInt"))
57-
// private val ULONG_PROGRESSION_ITERATOR_VALUE =
5858
// ProgressionIteratorBasicValue("ULong", Type.LONG_TYPE, Type.getObjectType("kotlin/ULong"))
5959

60-
private val PROGRESSION_CLASS_NAME_TO_ITERATOR_VALUE: Map<String, ProgressionIteratorBasicValue> =
61-
hashMapOf(
62-
CHAR_RANGE_FQN to CHAR_PROGRESSION_ITERATOR_VALUE,
63-
CHAR_PROGRESSION_FQN to CHAR_PROGRESSION_ITERATOR_VALUE,
64-
INT_RANGE_FQN to INT_PROGRESSION_ITERATOR_VALUE,
65-
INT_PROGRESSION_FQN to INT_PROGRESSION_ITERATOR_VALUE,
66-
LONG_RANGE_FQN to LONG_PROGRESSION_ITERATOR_VALUE,
67-
LONG_PROGRESSION_FQN to LONG_PROGRESSION_ITERATOR_VALUE,
68-
// UINT_RANGE_FQN to UINT_PROGRESSION_ITERATOR_VALUE,
69-
// UINT_PROGRESSION_FQN to UINT_PROGRESSION_ITERATOR_VALUE,
70-
// ULONG_RANGE_FQN to ULONG_PROGRESSION_ITERATOR_VALUE,
71-
// ULONG_PROGRESSION_FQN to ULONG_PROGRESSION_ITERATOR_VALUE
60+
private fun progressionIteratorValue(
61+
iteratorCallInsn: AbstractInsnNode,
62+
typeName: String,
63+
valuesPrimitiveType: Type,
64+
valuesBoxedType: Type = AsmUtil.boxType(valuesPrimitiveType)
65+
) =
66+
ProgressionIteratorBasicValue(
67+
iteratorCallInsn,
68+
"next$typeName",
69+
Type.getObjectType("kotlin/collections/${typeName}Iterator"),
70+
valuesPrimitiveType,
71+
valuesBoxedType
7272
)
7373

74-
fun byProgressionClassType(progressionClassType: Type): ProgressionIteratorBasicValue? =
75-
PROGRESSION_CLASS_NAME_TO_ITERATOR_VALUE[progressionClassType.className]
74+
fun byProgressionClassType(iteratorCallInsn: AbstractInsnNode, progressionClassType: Type): ProgressionIteratorBasicValue? =
75+
when (progressionClassType.className) {
76+
CHAR_RANGE_FQN, CHAR_PROGRESSION_FQN ->
77+
progressionIteratorValue(iteratorCallInsn, "Char", Type.CHAR_TYPE)
78+
INT_RANGE_FQN, INT_PROGRESSION_FQN ->
79+
progressionIteratorValue(iteratorCallInsn, "Int", Type.INT_TYPE)
80+
LONG_RANGE_FQN, LONG_PROGRESSION_FQN ->
81+
progressionIteratorValue(iteratorCallInsn, "Long", Type.LONG_TYPE)
82+
else ->
83+
null
84+
}
7685
}
7786
}
7887

compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt

+14-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
3333
import org.jetbrains.org.objectweb.asm.tree.*
3434
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
3535
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
36-
import java.util.*
3736

3837
class RedundantBoxingMethodTransformer(private val generationState: GenerationState) : MethodTransformer() {
3938

@@ -50,6 +49,9 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
5049
val valuesToOptimize = interpreter.candidatesBoxedValues
5150

5251
if (!valuesToOptimize.isEmpty) {
52+
// has side effect on valuesToOptimize
53+
removeValuesFromTaintedProgressionIterators(valuesToOptimize)
54+
5355
// has side effect on valuesToOptimize and frames, containing BoxedBasicValues that are unsafe to remove
5456
removeValuesClashingWithVariables(valuesToOptimize, node, frames)
5557

@@ -86,7 +88,7 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
8688
private fun removeValuesClashingWithVariables(
8789
values: RedundantBoxedValuesCollection,
8890
node: MethodNode,
89-
frames: Array<Frame<BasicValue>>
91+
frames: Array<Frame<BasicValue>?>
9092
) {
9193
while (removeValuesClashingWithVariablesPass(values, node, frames)) {
9294
// do nothing
@@ -126,6 +128,15 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
126128
return needToRepeat
127129
}
128130

131+
private fun removeValuesFromTaintedProgressionIterators(valuesToOptimize: RedundantBoxedValuesCollection) {
132+
for (descriptor in valuesToOptimize.toList()) {
133+
val progressionIterator = descriptor?.progressionIterator ?: continue
134+
if (progressionIterator.tainted) {
135+
valuesToOptimize.remove(descriptor)
136+
}
137+
}
138+
}
139+
129140
private fun isUnsafeToRemoveBoxingForConnectedValues(usedValues: List<BasicValue>, unboxedType: Type): Boolean =
130141
usedValues.any { input ->
131142
if (input === StrictBasicValue.UNINITIALIZED_VALUE) return@any false
@@ -135,7 +146,7 @@ class RedundantBoxingMethodTransformer(private val generationState: GenerationSt
135146
!descriptor.isSafeToRemove || descriptor.unboxedType != unboxedType
136147
}
137148

138-
private fun adaptLocalVariableTableForBoxedValues(node: MethodNode, frames: Array<Frame<BasicValue>>) {
149+
private fun adaptLocalVariableTableForBoxedValues(node: MethodNode, frames: Array<Frame<BasicValue>?>) {
139150
for (localVariableNode in node.localVariables) {
140151
if (Type.getType(localVariableNode.desc).sort != Type.OBJECT) {
141152
continue

compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/NullabilityInterpreter.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class NullabilityInterpreter(private val generationState: GenerationState) : Opt
8484
insn.isBoxing(generationState) ->
8585
NotNullBasicValue(resultType)
8686
insn.isIteratorMethodCallOfProgression(values) ->
87-
ProgressionIteratorBasicValue.byProgressionClassType(values[0].type)
87+
ProgressionIteratorBasicValue.byProgressionClassType(insn, values[0].type)
8888
insn.isNextMethodCallOfProgressionIterator(values) ->
8989
NotNullBasicValue(resultType)
9090
insn.isPseudo(PseudoInsn.AS_NOT_NULL) ->

compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// IGNORE_BACKEND: WASM
2+
// WITH_RUNTIME
3+
4+
val p0 = 0..3
5+
6+
fun test(): List<Int> {
7+
val progression = if (p0.last == 3) p0 + 1 else p0
8+
return progression.map { it }
9+
}
10+
11+
fun box(): String {
12+
val t = test()
13+
if (t != listOf(0, 1, 2, 3, 1))
14+
return "Failed: t=$t"
15+
return "OK"
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// IGNORE_BACKEND: WASM
2+
// WITH_RUNTIME
3+
import kotlin.test.assertEquals
4+
5+
fun test() {
6+
var i = 0
7+
val a = ubyteArrayOf(3u, 2u, 1u)
8+
a.forEach { e -> assertEquals(e, a[i++]) }
9+
}
10+
11+
fun box(): String {
12+
test()
13+
return "OK"
14+
}

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)