KT-15862 Inline generic functions can unexpectedly box primitives
Previous version of the boxing/unboxing analysis treated merging boxed and non-boxed values as a hazard.
If such merged values are not used (e.g., early return + local variables reused in inlined calls),
corresponding boxing/unboxing operations still can be optimized out.
All information related to boxed value usage by instructions is moved to 'BoxedValueDescriptor'.
Introduce "tainted" (and "clean") boxed values, with the following rules:
merge(B, B) = B, if unboxed types are compatible,
T, otherwise
merge(B, X) = T
merge(T, X) = T
where
X is a non-boxed value,
B is a "clean" boxed value,
T is a "tainted" boxed value.
Postpone decision about value merge hazards until a "tainted" value is used.
This commit is contained in:
+49
-34
@@ -20,35 +20,53 @@ import com.intellij.openapi.util.Pair
|
|||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
|
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class BoxedBasicValue(
|
abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) {
|
||||||
|
abstract val descriptor: BoxedValueDescriptor
|
||||||
|
abstract fun taint(): BoxedBasicValue
|
||||||
|
|
||||||
|
override fun equals(other: Any?) = this === other
|
||||||
|
override fun hashCode() = System.identityHashCode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CleanBoxedValue(
|
||||||
boxedType: Type,
|
boxedType: Type,
|
||||||
|
boxingInsn: AbstractInsnNode,
|
||||||
|
progressionIterator: ProgressionIteratorBasicValue?
|
||||||
|
) : BoxedBasicValue(boxedType) {
|
||||||
|
override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator)
|
||||||
|
|
||||||
|
private var tainted: TaintedBoxedValue? = null
|
||||||
|
override fun taint(): BoxedBasicValue = tainted ?: TaintedBoxedValue(this).also { tainted = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TaintedBoxedValue(val boxedBasicValue: CleanBoxedValue) : BoxedBasicValue(boxedBasicValue.type) {
|
||||||
|
override val descriptor get() = boxedBasicValue.descriptor
|
||||||
|
|
||||||
|
override fun taint(): BoxedBasicValue = this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BoxedValueDescriptor(
|
||||||
|
val boxedType: Type,
|
||||||
val boxingInsn: AbstractInsnNode,
|
val boxingInsn: AbstractInsnNode,
|
||||||
val progressionIterator: ProgressionIteratorBasicValue?
|
val progressionIterator: ProgressionIteratorBasicValue?
|
||||||
) : StrictBasicValue(boxedType) {
|
) {
|
||||||
private val associatedInsns = HashSet<AbstractInsnNode>()
|
private val associatedInsns = HashSet<AbstractInsnNode>()
|
||||||
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
||||||
private val associatedVariables = HashSet<Int>()
|
private val associatedVariables = HashSet<Int>()
|
||||||
private val mergedWith = HashSet<BoxedBasicValue>()
|
private val mergedWith = HashSet<BoxedValueDescriptor>()
|
||||||
|
|
||||||
val primitiveType: Type = unboxType(boxedType)
|
|
||||||
var isSafeToRemove = true; private set
|
var isSafeToRemove = true; private set
|
||||||
|
val unboxedType: Type = getUnboxedType(boxedType)
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
fun getAssociatedInsns() = associatedInsns.toReadOnlyList()
|
||||||
this === other
|
|
||||||
|
|
||||||
fun typeEquals(other: BasicValue) =
|
|
||||||
other is BoxedBasicValue && type == other.type
|
|
||||||
|
|
||||||
override fun hashCode() =
|
|
||||||
System.identityHashCode(this)
|
|
||||||
|
|
||||||
fun getAssociatedInsns(): List<AbstractInsnNode> =
|
|
||||||
ArrayList(associatedInsns)
|
|
||||||
|
|
||||||
fun addInsn(insnNode: AbstractInsnNode) {
|
fun addInsn(insnNode: AbstractInsnNode) {
|
||||||
associatedInsns.add(insnNode)
|
associatedInsns.add(insnNode)
|
||||||
@@ -61,22 +79,20 @@ class BoxedBasicValue(
|
|||||||
fun getVariablesIndexes(): List<Int> =
|
fun getVariablesIndexes(): List<Int> =
|
||||||
ArrayList(associatedVariables)
|
ArrayList(associatedVariables)
|
||||||
|
|
||||||
fun addMergedWith(value: BoxedBasicValue) {
|
fun addMergedWith(descriptor: BoxedValueDescriptor) {
|
||||||
mergedWith.add(value)
|
mergedWith.add(descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMergedWith(): Iterable<BoxedBasicValue> =
|
fun getMergedWith(): Iterable<BoxedValueDescriptor> =
|
||||||
mergedWith
|
mergedWith
|
||||||
|
|
||||||
fun markAsUnsafeToRemove() {
|
fun markAsUnsafeToRemove() {
|
||||||
isSafeToRemove = false
|
isSafeToRemove = false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDoubleSize() =
|
fun isDoubleSize() = unboxedType.size == 2
|
||||||
primitiveType.size == 2
|
|
||||||
|
|
||||||
fun isFromProgressionIterator() =
|
fun isFromProgressionIterator() = progressionIterator != null
|
||||||
progressionIterator != null
|
|
||||||
|
|
||||||
fun addUnboxingWithCastTo(insn: AbstractInsnNode, type: Type) {
|
fun addUnboxingWithCastTo(insn: AbstractInsnNode, type: Type) {
|
||||||
unboxingWithCastInsns.add(Pair.create(insn, type))
|
unboxingWithCastInsns.add(Pair.create(insn, type))
|
||||||
@@ -84,15 +100,14 @@ class BoxedBasicValue(
|
|||||||
|
|
||||||
fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> =
|
fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> =
|
||||||
unboxingWithCastInsns
|
unboxingWithCastInsns
|
||||||
|
}
|
||||||
companion object {
|
|
||||||
private fun unboxType(boxedType: Type): Type {
|
|
||||||
val primitiveType = AsmUtil.unboxPrimitiveTypeOrNull(boxedType)
|
fun getUnboxedType(boxedType: Type): Type {
|
||||||
if (primitiveType != null) return primitiveType
|
val primitiveType = AsmUtil.unboxPrimitiveTypeOrNull(boxedType)
|
||||||
|
if (primitiveType != null) return primitiveType
|
||||||
if (boxedType == AsmTypes.K_CLASS_TYPE) return AsmTypes.JAVA_CLASS_TYPE
|
|
||||||
|
if (boxedType == AsmTypes.K_CLASS_TYPE) return AsmTypes.JAVA_CLASS_TYPE
|
||||||
throw IllegalArgumentException("Expected primitive type wrapper or KClass, got: $boxedType")
|
|
||||||
}
|
throw IllegalArgumentException("Expected primitive type wrapper or KClass, got: $boxedType")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-27
@@ -29,7 +29,6 @@ import org.jetbrains.org.objectweb.asm.Type
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -39,14 +38,23 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue {
|
protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue {
|
||||||
val index = insnList.indexOf(insn)
|
val index = insnList.indexOf(insn)
|
||||||
return boxingPlaces.getOrPut(index) {
|
return boxingPlaces.getOrPut(index) {
|
||||||
val boxedBasicValue = BoxedBasicValue(type, insn, progressionIterator)
|
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator)
|
||||||
onNewBoxedValue(boxedBasicValue)
|
onNewBoxedValue(boxedBasicValue)
|
||||||
boxedBasicValue
|
boxedBasicValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(AnalyzerException::class)
|
protected fun checkUsedValue(value: BasicValue) {
|
||||||
|
if (value is TaintedBoxedValue) {
|
||||||
|
onMergeFail(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun naryOperation(insn: AbstractInsnNode, values: List<BasicValue>): BasicValue? {
|
override fun naryOperation(insn: AbstractInsnNode, values: List<BasicValue>): BasicValue? {
|
||||||
|
values.forEach {
|
||||||
|
checkUsedValue(it)
|
||||||
|
}
|
||||||
|
|
||||||
val value = super.naryOperation(insn, values)
|
val value = super.naryOperation(insn, values)
|
||||||
val firstArg = values.firstOrNull() ?: return value
|
val firstArg = values.firstOrNull() ?: return value
|
||||||
|
|
||||||
@@ -80,36 +88,39 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(AnalyzerException::class)
|
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
||||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
|
checkUsedValue(value)
|
||||||
if (insn.opcode == Opcodes.CHECKCAST && isExactValue(value))
|
|
||||||
value
|
return if (insn.opcode == Opcodes.CHECKCAST && isExactValue(value))
|
||||||
else
|
value
|
||||||
super.unaryOperation(insn, value)
|
else
|
||||||
|
super.unaryOperation(insn, value)
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun isExactValue(value: BasicValue) =
|
protected open fun isExactValue(value: BasicValue) =
|
||||||
value is ProgressionIteratorBasicValue ||
|
value is ProgressionIteratorBasicValue ||
|
||||||
value is BoxedBasicValue ||
|
value is CleanBoxedValue ||
|
||||||
value.type != null && isProgressionClass(value.type.internalName)
|
value.type != null && isProgressionClass(value.type.internalName)
|
||||||
|
|
||||||
override fun merge(v: BasicValue, w: BasicValue) =
|
override fun merge(v: BasicValue, w: BasicValue) =
|
||||||
when {
|
when {
|
||||||
v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE -> {
|
v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE ->
|
||||||
StrictBasicValue.UNINITIALIZED_VALUE
|
StrictBasicValue.UNINITIALIZED_VALUE
|
||||||
}
|
v is BoxedBasicValue && w is BoxedBasicValue -> {
|
||||||
v is BoxedBasicValue && v.typeEquals(w) -> {
|
onMergeSuccess(v, w)
|
||||||
onMergeSuccess(v, w as BoxedBasicValue)
|
when {
|
||||||
v
|
v is TaintedBoxedValue -> v
|
||||||
}
|
w is TaintedBoxedValue -> w
|
||||||
else -> {
|
v.type != w.type -> v.taint()
|
||||||
if (v is BoxedBasicValue) {
|
else -> v
|
||||||
onMergeFail(v)
|
|
||||||
}
|
|
||||||
if (w is BoxedBasicValue) {
|
|
||||||
onMergeFail(w)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
v is BoxedBasicValue ->
|
||||||
|
v.taint()
|
||||||
|
w is BoxedBasicValue ->
|
||||||
|
w.taint()
|
||||||
|
else ->
|
||||||
super.merge(v, w)
|
super.merge(v, w)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
|
protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
|
||||||
@@ -197,7 +208,5 @@ private fun isProgressionClass(internalClassName: String) =
|
|||||||
RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName))
|
RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName))
|
||||||
|
|
||||||
private fun getValuesTypeOfProgressionClass(progressionClassInternalName: String) =
|
private fun getValuesTypeOfProgressionClass(progressionClassInternalName: String) =
|
||||||
RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName))?.let {
|
RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName))
|
||||||
type ->
|
?.typeName?.asString() ?: error("type should be not null")
|
||||||
type.typeName.asString()
|
|
||||||
} ?: error("type should be not null")
|
|
||||||
|
|||||||
+12
-12
@@ -22,25 +22,25 @@ import java.util.HashSet;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class RedundantBoxedValuesCollection implements Iterable<BoxedBasicValue> {
|
public class RedundantBoxedValuesCollection implements Iterable<BoxedValueDescriptor> {
|
||||||
private final Set<BoxedBasicValue> safeToDeleteValues = new HashSet<BoxedBasicValue>();
|
private final Set<BoxedValueDescriptor> safeToDeleteValues = new HashSet<BoxedValueDescriptor>();
|
||||||
|
|
||||||
public void add(@NotNull BoxedBasicValue value) {
|
public void add(@NotNull BoxedValueDescriptor descriptor) {
|
||||||
safeToDeleteValues.add(value);
|
safeToDeleteValues.add(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(@NotNull BoxedBasicValue value) {
|
public void remove(@NotNull BoxedValueDescriptor descriptor) {
|
||||||
if (safeToDeleteValues.contains(value)) {
|
if (safeToDeleteValues.contains(descriptor)) {
|
||||||
safeToDeleteValues.remove(value);
|
safeToDeleteValues.remove(descriptor);
|
||||||
value.markAsUnsafeToRemove();
|
descriptor.markAsUnsafeToRemove();
|
||||||
|
|
||||||
for (BoxedBasicValue mergedValue : value.getMergedWith()) {
|
for (BoxedValueDescriptor mergedValueDescriptor : descriptor.getMergedWith()) {
|
||||||
remove(mergedValue);
|
remove(mergedValueDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void merge(@NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w) {
|
public void merge(@NotNull BoxedValueDescriptor v, @NotNull BoxedValueDescriptor w) {
|
||||||
v.addMergedWith(w);
|
v.addMergedWith(w);
|
||||||
w.addMergedWith(v);
|
w.addMergedWith(v);
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ public class RedundantBoxedValuesCollection implements Iterable<BoxedBasicValue>
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Iterator<BoxedBasicValue> iterator() {
|
public Iterator<BoxedValueDescriptor> iterator() {
|
||||||
return safeToDeleteValues.iterator();
|
return safeToDeleteValues.iterator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-36
@@ -23,30 +23,12 @@ import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
||||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
|
|
||||||
internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterpreter(insnList) {
|
internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterpreter(insnList) {
|
||||||
|
|
||||||
val candidatesBoxedValues = RedundantBoxedValuesCollection()
|
val candidatesBoxedValues = RedundantBoxedValuesCollection()
|
||||||
|
|
||||||
@Throws(AnalyzerException::class)
|
|
||||||
override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? {
|
|
||||||
processOperationWithBoxedValue(value1, insn)
|
|
||||||
processOperationWithBoxedValue(value2, insn)
|
|
||||||
|
|
||||||
return super.binaryOperation(insn, value1, value2)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(AnalyzerException::class)
|
|
||||||
override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? {
|
|
||||||
// in a valid code only aastore could happen with boxed value
|
|
||||||
processOperationWithBoxedValue(value3, insn)
|
|
||||||
|
|
||||||
return super.ternaryOperation(insn, value1, value2, value3)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(AnalyzerException::class)
|
|
||||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
||||||
if ((insn.opcode == Opcodes.CHECKCAST || insn.opcode == Opcodes.INSTANCEOF) && value is BoxedBasicValue) {
|
if ((insn.opcode == Opcodes.CHECKCAST || insn.opcode == Opcodes.INSTANCEOF) && value is BoxedBasicValue) {
|
||||||
val typeInsn = insn as TypeInsnNode
|
val typeInsn = insn as TypeInsnNode
|
||||||
@@ -61,10 +43,23 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
|||||||
return super.unaryOperation(insn, value)
|
return super.unaryOperation(insn, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(AnalyzerException::class)
|
override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? {
|
||||||
|
processOperationWithBoxedValue(value1, insn)
|
||||||
|
processOperationWithBoxedValue(value2, insn)
|
||||||
|
|
||||||
|
return super.binaryOperation(insn, value1, value2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? {
|
||||||
|
// in a valid code only aastore could happen with boxed value
|
||||||
|
processOperationWithBoxedValue(value3, insn)
|
||||||
|
|
||||||
|
return super.ternaryOperation(insn, value1, value2, value3)
|
||||||
|
}
|
||||||
|
|
||||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue {
|
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue {
|
||||||
if (value is BoxedBasicValue && insn.opcode === Opcodes.ASTORE) {
|
if (value is BoxedBasicValue && insn.opcode == Opcodes.ASTORE) {
|
||||||
value.addVariableIndex((insn as VarInsnNode).`var`)
|
value.descriptor.addVariableIndex((insn as VarInsnNode).`var`)
|
||||||
}
|
}
|
||||||
|
|
||||||
processOperationWithBoxedValue(value, insn)
|
processOperationWithBoxedValue(value, insn)
|
||||||
@@ -77,15 +72,15 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewBoxedValue(value: BoxedBasicValue) {
|
override fun onNewBoxedValue(value: BoxedBasicValue) {
|
||||||
candidatesBoxedValues.add(value)
|
candidatesBoxedValues.add(value.descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {
|
override fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {
|
||||||
if (value.primitiveType == resultType) {
|
value.descriptor.run {
|
||||||
addAssociatedInsn(value, insn)
|
if (unboxedType == resultType)
|
||||||
}
|
addAssociatedInsn(value, insn)
|
||||||
else {
|
else
|
||||||
value.addUnboxingWithCastTo(insn, resultType)
|
addUnboxingWithCastTo(insn, resultType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,11 +93,13 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) {
|
override fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) {
|
||||||
candidatesBoxedValues.merge(v, w)
|
candidatesBoxedValues.merge(v.descriptor, w.descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processOperationWithBoxedValue(value: BasicValue?, insnNode: AbstractInsnNode) {
|
private fun processOperationWithBoxedValue(value: BasicValue?, insnNode: AbstractInsnNode) {
|
||||||
if (value is BoxedBasicValue) {
|
if (value is BoxedBasicValue) {
|
||||||
|
checkUsedValue(value)
|
||||||
|
|
||||||
if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) {
|
if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) {
|
||||||
markValueAsDirty(value)
|
markValueAsDirty(value)
|
||||||
}
|
}
|
||||||
@@ -113,7 +110,7 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun markValueAsDirty(value: BoxedBasicValue) {
|
private fun markValueAsDirty(value: BoxedBasicValue) {
|
||||||
candidatesBoxedValues.remove(value)
|
candidatesBoxedValues.remove(value.descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -127,17 +124,15 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
|||||||
when (targetInternalName) {
|
when (targetInternalName) {
|
||||||
Type.getInternalName(Any::class.java) ->
|
Type.getInternalName(Any::class.java) ->
|
||||||
true
|
true
|
||||||
Type.getInternalName(Number::class.java) -> {
|
Type.getInternalName(Number::class.java) ->
|
||||||
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(
|
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort)
|
||||||
value.primitiveType.sort)
|
|
||||||
}
|
|
||||||
else ->
|
else ->
|
||||||
value.type.internalName.equals(targetInternalName)
|
value.type.internalName == targetInternalName
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) {
|
private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) {
|
||||||
if (value.isSafeToRemove) {
|
value.descriptor.run {
|
||||||
value.addInsn(insn)
|
if (isSafeToRemove) addInsn(insn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-35
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
import com.google.common.collect.Collections2;
|
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
import kotlin.collections.CollectionsKt;
|
import kotlin.collections.CollectionsKt;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
@@ -38,9 +36,8 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
@Override
|
@Override
|
||||||
public void transform(@NotNull String internalClassName, @NotNull MethodNode node) {
|
public void transform(@NotNull String internalClassName, @NotNull MethodNode node) {
|
||||||
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
||||||
Frame<BasicValue>[] frames = analyze(
|
Frame<BasicValue>[] frames = analyze(internalClassName, node, interpreter);
|
||||||
internalClassName, node, interpreter
|
|
||||||
);
|
|
||||||
interpretPopInstructionsForBoxedValues(interpreter, node, frames);
|
interpretPopInstructionsForBoxedValues(interpreter, node, frames);
|
||||||
|
|
||||||
RedundantBoxedValuesCollection valuesToOptimize = interpreter.getCandidatesBoxedValues();
|
RedundantBoxedValuesCollection valuesToOptimize = interpreter.getCandidatesBoxedValues();
|
||||||
@@ -99,32 +96,25 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BasicValue> usedValues = getValuesStoredOrLoadedToVariable(localVariableNode, node, frames);
|
List<BasicValue> variableValues = getValuesStoredOrLoadedToVariable(localVariableNode, node, frames);
|
||||||
|
|
||||||
Collection<BasicValue> boxed = Collections2.filter(usedValues, new Predicate<BasicValue>() {
|
Collection<BasicValue> boxed = CollectionsKt.filter(variableValues, new Function1<BasicValue, Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(BasicValue input) {
|
public Boolean invoke(BasicValue value) {
|
||||||
return input instanceof BoxedBasicValue;
|
return value instanceof BoxedBasicValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (boxed.isEmpty()) continue;
|
if (boxed.isEmpty()) continue;
|
||||||
|
|
||||||
final BoxedBasicValue firstBoxed = (BoxedBasicValue) boxed.iterator().next();
|
BoxedValueDescriptor firstBoxed = ((BoxedBasicValue) boxed.iterator().next()).getDescriptor();
|
||||||
|
if (isUnsafeToRemoveBoxingForConnectedValues(variableValues, firstBoxed.getUnboxedType())) {
|
||||||
|
for (BasicValue value : variableValues) {
|
||||||
|
if (!(value instanceof BoxedBasicValue)) continue;
|
||||||
|
|
||||||
if (CollectionsKt.any(usedValues, new Function1<BasicValue, Boolean>() {
|
BoxedValueDescriptor descriptor = ((BoxedBasicValue) value).getDescriptor();
|
||||||
@Override
|
if (descriptor.isSafeToRemove()) {
|
||||||
public Boolean invoke(BasicValue input) {
|
values.remove(descriptor);
|
||||||
if (input == StrictBasicValue.UNINITIALIZED_VALUE) return false;
|
|
||||||
return input == null ||
|
|
||||||
!(input instanceof BoxedBasicValue) ||
|
|
||||||
!((BoxedBasicValue) input).isSafeToRemove() ||
|
|
||||||
!((BoxedBasicValue) input).getPrimitiveType().equals(firstBoxed.getPrimitiveType());
|
|
||||||
}
|
|
||||||
})) {
|
|
||||||
for (BasicValue value : usedValues) {
|
|
||||||
if (value instanceof BoxedBasicValue && ((BoxedBasicValue) value).isSafeToRemove()) {
|
|
||||||
values.remove((BoxedBasicValue) value);
|
|
||||||
needToRepeat = true;
|
needToRepeat = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,6 +124,20 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
return needToRepeat;
|
return needToRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isUnsafeToRemoveBoxingForConnectedValues(List<BasicValue> usedValues, final Type unboxedType) {
|
||||||
|
return CollectionsKt.any(usedValues, new Function1<BasicValue, Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean invoke(BasicValue input) {
|
||||||
|
if (input == StrictBasicValue.UNINITIALIZED_VALUE) return false;
|
||||||
|
if (!(input instanceof BoxedBasicValue)) return true;
|
||||||
|
|
||||||
|
BoxedValueDescriptor descriptor = ((BoxedBasicValue) input).getDescriptor();
|
||||||
|
return !descriptor.isSafeToRemove() ||
|
||||||
|
!(descriptor.getUnboxedType().equals(unboxedType));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static void adaptLocalVariableTableForBoxedValues(@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames) {
|
private static void adaptLocalVariableTableForBoxedValues(@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames) {
|
||||||
for (LocalVariableNode localVariableNode : node.localVariables) {
|
for (LocalVariableNode localVariableNode : node.localVariables) {
|
||||||
if (Type.getType(localVariableNode.desc).getSort() != Type.OBJECT) {
|
if (Type.getType(localVariableNode.desc).getSort() != Type.OBJECT) {
|
||||||
@@ -141,8 +145,11 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (BasicValue value : getValuesStoredOrLoadedToVariable(localVariableNode, node, frames)) {
|
for (BasicValue value : getValuesStoredOrLoadedToVariable(localVariableNode, node, frames)) {
|
||||||
if (value == null || !(value instanceof BoxedBasicValue) || !((BoxedBasicValue) value).isSafeToRemove()) continue;
|
if (!(value instanceof BoxedBasicValue)) continue;
|
||||||
localVariableNode.desc = ((BoxedBasicValue) value).getPrimitiveType().getDescriptor();
|
|
||||||
|
BoxedValueDescriptor descriptor = ((BoxedBasicValue) value).getDescriptor();
|
||||||
|
if (!descriptor.isSafeToRemove()) continue;
|
||||||
|
localVariableNode.desc = descriptor.getUnboxedType().getDescriptor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,9 +200,9 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static int[] buildVariablesRemapping(@NotNull RedundantBoxedValuesCollection values, @NotNull MethodNode node) {
|
private static int[] buildVariablesRemapping(@NotNull RedundantBoxedValuesCollection values, @NotNull MethodNode node) {
|
||||||
Set<Integer> doubleSizedVars = new HashSet<Integer>();
|
Set<Integer> doubleSizedVars = new HashSet<Integer>();
|
||||||
for (BoxedBasicValue value : values) {
|
for (BoxedValueDescriptor valueDescriptor : values) {
|
||||||
if (value.getPrimitiveType().getSize() == 2) {
|
if (valueDescriptor.isDoubleSize()) {
|
||||||
doubleSizedVars.addAll(value.getVariablesIndexes());
|
doubleSizedVars.addAll(valueDescriptor.getVariablesIndexes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,12 +240,12 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
@NotNull MethodNode node,
|
@NotNull MethodNode node,
|
||||||
@NotNull RedundantBoxedValuesCollection values
|
@NotNull RedundantBoxedValuesCollection values
|
||||||
) {
|
) {
|
||||||
for (BoxedBasicValue value : values) {
|
for (BoxedValueDescriptor value : values) {
|
||||||
adaptInstructionsForBoxedValue(node, value);
|
adaptInstructionsForBoxedValue(node, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void adaptInstructionsForBoxedValue(@NotNull MethodNode node, @NotNull BoxedBasicValue value) {
|
private static void adaptInstructionsForBoxedValue(@NotNull MethodNode node, @NotNull BoxedValueDescriptor value) {
|
||||||
adaptBoxingInstruction(node, value);
|
adaptBoxingInstruction(node, value);
|
||||||
|
|
||||||
for (Pair<AbstractInsnNode, Type> cast : value.getUnboxingWithCastInsns()) {
|
for (Pair<AbstractInsnNode, Type> cast : value.getUnboxingWithCastInsns()) {
|
||||||
@@ -250,7 +257,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void adaptBoxingInstruction(@NotNull MethodNode node, @NotNull BoxedBasicValue value) {
|
private static void adaptBoxingInstruction(@NotNull MethodNode node, @NotNull BoxedValueDescriptor value) {
|
||||||
if (!value.isFromProgressionIterator()) {
|
if (!value.isFromProgressionIterator()) {
|
||||||
node.instructions.remove(value.getBoxingInsn());
|
node.instructions.remove(value.getBoxingInsn());
|
||||||
}
|
}
|
||||||
@@ -280,12 +287,12 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
|
|
||||||
private static void adaptCastInstruction(
|
private static void adaptCastInstruction(
|
||||||
@NotNull MethodNode node,
|
@NotNull MethodNode node,
|
||||||
@NotNull BoxedBasicValue value,
|
@NotNull BoxedValueDescriptor value,
|
||||||
@NotNull Pair<AbstractInsnNode, Type> castWithType
|
@NotNull Pair<AbstractInsnNode, Type> castWithType
|
||||||
) {
|
) {
|
||||||
AbstractInsnNode castInsn = castWithType.getFirst();
|
AbstractInsnNode castInsn = castWithType.getFirst();
|
||||||
MethodNode castInsnsListener = new MethodNode(Opcodes.ASM5);
|
MethodNode castInsnsListener = new MethodNode(Opcodes.ASM5);
|
||||||
new InstructionAdapter(castInsnsListener).cast(value.getPrimitiveType(), castWithType.getSecond());
|
new InstructionAdapter(castInsnsListener).cast(value.getUnboxedType(), castWithType.getSecond());
|
||||||
|
|
||||||
for (AbstractInsnNode insn : castInsnsListener.instructions.toArray()) {
|
for (AbstractInsnNode insn : castInsnsListener.instructions.toArray()) {
|
||||||
node.instructions.insertBefore(castInsn, insn);
|
node.instructions.insertBefore(castInsn, insn);
|
||||||
@@ -295,7 +302,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void adaptInstruction(
|
private static void adaptInstruction(
|
||||||
@NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value
|
@NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedValueDescriptor value
|
||||||
) {
|
) {
|
||||||
boolean isDoubleSize = value.isDoubleSize();
|
boolean isDoubleSize = value.isDoubleSize();
|
||||||
|
|
||||||
@@ -322,7 +329,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
node.instructions.set(
|
node.instructions.set(
|
||||||
insn,
|
insn,
|
||||||
new VarInsnNode(
|
new VarInsnNode(
|
||||||
value.getPrimitiveType().getOpcode(intVarOpcode),
|
value.getUnboxedType().getOpcode(intVarOpcode),
|
||||||
((VarInsnNode) insn).var
|
((VarInsnNode) insn).var
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
// Just make sure there's no VerifyError
|
||||||
|
|
||||||
|
fun getOrElse() =
|
||||||
|
mapOf<String, Int>().getOrElse("foo") { 3 }
|
||||||
|
|
||||||
|
fun isNotEmpty(l: ArrayList<Int>) =
|
||||||
|
l.iterator()?.hasNext() ?: false
|
||||||
|
|
||||||
|
fun box() = "OK"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
inline fun <T> put(
|
||||||
|
x: T,
|
||||||
|
maxExclusive: Int,
|
||||||
|
isEmpty: (Int) -> Boolean,
|
||||||
|
equals: (T, T) -> Boolean,
|
||||||
|
fetch: (Int) -> T,
|
||||||
|
store: (Int, T) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
var i = 0
|
||||||
|
do {
|
||||||
|
if (isEmpty(i)) {
|
||||||
|
store(i, x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val y = fetch(i)
|
||||||
|
if (equals(x, y)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
if (i >= maxExclusive) return false
|
||||||
|
} while (true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const val SIZE = 16
|
||||||
|
val arr = IntArray(SIZE) { -1 }
|
||||||
|
|
||||||
|
fun putNonNegInt(x: Int) =
|
||||||
|
put(x, SIZE,
|
||||||
|
isEmpty = { arr[it] < 0 },
|
||||||
|
equals = { x, y -> x == y },
|
||||||
|
fetch = { arr[it] },
|
||||||
|
store = { i, x -> arr[i] = x }
|
||||||
|
)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
putNonNegInt(1)
|
||||||
|
putNonNegInt(2)
|
||||||
|
putNonNegInt(3)
|
||||||
|
|
||||||
|
if (arr[0] != 1) return "Fail, ${arr.toList().toString()}"
|
||||||
|
if (arr[1] != 2) return "Fail, ${arr.toList().toString()}"
|
||||||
|
if (arr[2] != 3) return "Fail, ${arr.toList().toString()}"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
// @TestKt.class:
|
||||||
|
// 0 valueOf
|
||||||
|
// 0 Value\s\(\)
|
||||||
|
|
||||||
|
val mask = 127
|
||||||
|
val entries = IntArray(128)
|
||||||
|
val flags = BooleanArray(128)
|
||||||
|
|
||||||
|
fun distance(index: Int, hash: Int): Int = (128 + index - (hash and mask)) and mask
|
||||||
|
|
||||||
|
fun insertSad(x: Int): Boolean {
|
||||||
|
return insertWithBoxing(
|
||||||
|
x,
|
||||||
|
hash = { it },
|
||||||
|
equals = { a, b -> a == b },
|
||||||
|
isEmpty = { !flags[it] },
|
||||||
|
fetch = { entries[it] },
|
||||||
|
store = { i, x -> entries[i] = x; flags[i] = true; }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: inline.kt
|
||||||
|
inline fun <T> insertWithBoxing(entry: T,
|
||||||
|
hash: (T) -> Int,
|
||||||
|
equals: (T, T) -> Boolean,
|
||||||
|
isEmpty: (Int) -> Boolean,
|
||||||
|
fetch: (Int) -> T,
|
||||||
|
store: (Int, T) -> Unit): Boolean {
|
||||||
|
var currentEntry = entry
|
||||||
|
var index = hash(entry) and mask
|
||||||
|
var dist = 0
|
||||||
|
do {
|
||||||
|
if (isEmpty(index)) {
|
||||||
|
store(index, currentEntry)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val existingEntry = fetch(index)
|
||||||
|
if (equals(existingEntry, currentEntry)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val existingHash = hash(existingEntry)
|
||||||
|
val existingDistance = distance(index, existingHash)
|
||||||
|
if (existingDistance < dist) {
|
||||||
|
store(index, currentEntry)
|
||||||
|
currentEntry = existingEntry
|
||||||
|
dist = existingDistance
|
||||||
|
}
|
||||||
|
|
||||||
|
dist += 1
|
||||||
|
index = (index + 1) and mask
|
||||||
|
}
|
||||||
|
while (true)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
// @TestKt.class:
|
||||||
|
// 0 valueOf
|
||||||
|
// 0 Value\s\(\)
|
||||||
|
|
||||||
|
const val SIZE = 16
|
||||||
|
val arr = IntArray(SIZE) { -1 }
|
||||||
|
|
||||||
|
fun putNonNegInt(x: Int) =
|
||||||
|
put(x, SIZE,
|
||||||
|
isEmpty = { arr[it] < 0 },
|
||||||
|
equals = { x, y -> x == y },
|
||||||
|
fetch = { arr[it] },
|
||||||
|
store = { i, x -> arr[i] = x }
|
||||||
|
)
|
||||||
|
|
||||||
|
// FILE: inline.kt
|
||||||
|
inline fun <T> put(
|
||||||
|
x: T,
|
||||||
|
maxExclusive: Int,
|
||||||
|
isEmpty: (Int) -> Boolean,
|
||||||
|
equals: (T, T) -> Boolean,
|
||||||
|
fetch: (Int) -> T,
|
||||||
|
store: (Int, T) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
var i = 0
|
||||||
|
do {
|
||||||
|
if (isEmpty(i)) {
|
||||||
|
store(i, x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val y = fetch(i)
|
||||||
|
if (equals(x, y)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
if (i >= maxExclusive) return false
|
||||||
|
} while (true)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public final class TaintedValuesKt {
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||||
|
public final static method getOrElse(): int
|
||||||
|
public final static method isNotEmpty(@org.jetbrains.annotations.NotNull p0: java.util.ArrayList): boolean
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public final class TaintedValuesBoxKt {
|
||||||
|
public final static field SIZE: int
|
||||||
|
private final static @org.jetbrains.annotations.NotNull field arr: int[]
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method getArr(): int[]
|
||||||
|
public final static method put(p0: java.lang.Object, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.NotNull p3: kotlin.jvm.functions.Function2, @org.jetbrains.annotations.NotNull p4: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.NotNull p5: kotlin.jvm.functions.Function2): boolean
|
||||||
|
public final static method putNonNegInt(p0: int): boolean
|
||||||
|
}
|
||||||
+12
@@ -938,6 +938,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValues.kt")
|
||||||
|
public void testTaintedValues() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValuesBox.kt")
|
||||||
|
public void testTaintedValuesBox() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unsafeRemoving.kt")
|
@TestMetadata("unsafeRemoving.kt")
|
||||||
public void testUnsafeRemoving() throws Exception {
|
public void testUnsafeRemoving() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
||||||
|
|||||||
@@ -938,6 +938,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValues.kt")
|
||||||
|
public void testTaintedValues() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValuesBox.kt")
|
||||||
|
public void testTaintedValuesBox() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unsafeRemoving.kt")
|
@TestMetadata("unsafeRemoving.kt")
|
||||||
public void testUnsafeRemoving() throws Exception {
|
public void testUnsafeRemoving() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
||||||
|
|||||||
@@ -473,6 +473,18 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt15862.kt")
|
||||||
|
public void testKt15862() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt15862_2.kt")
|
||||||
|
public void testKt15862_2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862_2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt6842.kt")
|
@TestMetadata("kt6842.kt")
|
||||||
public void testKt6842() throws Exception {
|
public void testKt6842() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/kt6842.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/kt6842.kt");
|
||||||
|
|||||||
+12
@@ -938,6 +938,18 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValues.kt")
|
||||||
|
public void testTaintedValues() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValuesBox.kt")
|
||||||
|
public void testTaintedValuesBox() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unsafeRemoving.kt")
|
@TestMetadata("unsafeRemoving.kt")
|
||||||
public void testUnsafeRemoving() throws Exception {
|
public void testUnsafeRemoving() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
||||||
|
|||||||
+12
@@ -1160,6 +1160,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValues.kt")
|
||||||
|
public void testTaintedValues() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("taintedValuesBox.kt")
|
||||||
|
public void testTaintedValuesBox() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unsafeRemoving.kt")
|
@TestMetadata("unsafeRemoving.kt")
|
||||||
public void testUnsafeRemoving() throws Exception {
|
public void testUnsafeRemoving() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user