mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-20 08:31:28 +00:00
Revert "Revert "Completely rewrite reifiedIntTypeAnalysis, making it more streamline""
This reverts commit 822c14814b.
This commit is contained in:
@@ -593,7 +593,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
private fun spillVariables(suspensionPoints: List<SuspensionPoint>, methodNode: MethodNode): List<List<SpilledVariableDescriptor>> {
|
||||
val instructions = methodNode.instructions
|
||||
val frames = performRefinedTypeAnalysis(methodNode, containingClassInternalName)
|
||||
val frames = performSpilledVariableFieldTypesAnalysis(methodNode, containingClassInternalName)
|
||||
fun AbstractInsnNode.index() = instructions.indexOf(this)
|
||||
|
||||
// We postpone these actions because they change instruction indices that we use when obtaining frames
|
||||
@@ -641,11 +641,11 @@ class CoroutineTransformerMethodVisitor(
|
||||
.map { Pair(it, frame.getLocal(it)) }
|
||||
.filter { (index, value) ->
|
||||
(index == 0 && needDispatchReceiver && isForNamedFunction) ||
|
||||
(value != StrictBasicValue.UNINITIALIZED_VALUE && livenessFrame.isAlive(index))
|
||||
(value.type != null && livenessFrame.isAlive(index))
|
||||
}
|
||||
|
||||
for ((index, basicValue) in variablesToSpill) {
|
||||
if (basicValue === StrictBasicValue.NULL_VALUE) {
|
||||
if (basicValue.type == NULL_TYPE) {
|
||||
postponedActions.add {
|
||||
with(instructions) {
|
||||
insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter {
|
||||
@@ -657,7 +657,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
continue
|
||||
}
|
||||
|
||||
val type = basicValue.type
|
||||
val type = basicValue.type!!
|
||||
val normalizedType = type.normalize()
|
||||
|
||||
val indexBySort = varsCountByType[normalizedType]?.plus(1) ?: 0
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
private class PossibleSpilledValue(val source: AbstractInsnNode, type: Type?) : BasicValue(type) {
|
||||
val usages = mutableSetOf<AbstractInsnNode>()
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.insnOpcodeText
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Handle
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||
|
||||
// BasicValue interpreter from ASM does not distinct 'int' types from other int-like types like 'byte' or 'boolean',
|
||||
// neither do HotSpot and JVM spec.
|
||||
// But it seems like Dalvik does not follow it, and spilling boolean value into an 'int' field fails with VerifyError on Android 4,
|
||||
// so this function calculates refined frames' markup.
|
||||
// Note that type of some values is only possible to determine by their usages (e.g. ICONST_1, BALOAD both may push boolean or byte on stack)
|
||||
// In this case, update the type of the value.
|
||||
|
||||
// StrictBasicValue with mutable type
|
||||
internal open class SpilledVariableFieldTypeValue(open var type: Type?, val insn: AbstractInsnNode?) : Value {
|
||||
override fun getSize(): Int = type?.size ?: 1
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is SpilledVariableFieldTypeValue && type == other.type && insn == other.insn
|
||||
|
||||
override fun hashCode(): Int = (type?.hashCode() ?: 0) xor insn.hashCode()
|
||||
|
||||
override fun toString() = if (type == null) "." else "$type"
|
||||
}
|
||||
|
||||
private class MergedSpilledVariableFieldTypeValue(
|
||||
val values: Set<SpilledVariableFieldTypeValue>
|
||||
) : SpilledVariableFieldTypeValue(null, null) {
|
||||
init {
|
||||
require(values.none { it is MergedSpilledVariableFieldTypeValue })
|
||||
}
|
||||
|
||||
override var type: Type?
|
||||
get() = values.first().type
|
||||
set(newType) {
|
||||
for (value in values) {
|
||||
value.type = newType
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is MergedSpilledVariableFieldTypeValue && other.values == values
|
||||
|
||||
override fun hashCode(): Int = values.hashCode()
|
||||
|
||||
override fun toString(): String = "M$values"
|
||||
}
|
||||
|
||||
private operator fun SpilledVariableFieldTypeValue?.plus(other: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? = when {
|
||||
this == null -> other
|
||||
other == null -> this
|
||||
this == other -> this
|
||||
this is MergedSpilledVariableFieldTypeValue -> {
|
||||
if (other is MergedSpilledVariableFieldTypeValue) MergedSpilledVariableFieldTypeValue(values + other.values)
|
||||
else MergedSpilledVariableFieldTypeValue(values + other)
|
||||
}
|
||||
other is MergedSpilledVariableFieldTypeValue -> MergedSpilledVariableFieldTypeValue(other.values + this)
|
||||
else -> MergedSpilledVariableFieldTypeValue(setOf(this, other))
|
||||
}
|
||||
|
||||
internal val NULL_TYPE = Type.getObjectType("null")
|
||||
|
||||
// Same as BasicInterpreter, but updates types based on usages
|
||||
private class SpilledVariableFieldTypesInterpreter(
|
||||
private val methodNode: MethodNode
|
||||
) : Interpreter<SpilledVariableFieldTypeValue>(API_VERSION) {
|
||||
override fun newValue(type: Type?): SpilledVariableFieldTypeValue? =
|
||||
if (type == Type.VOID_TYPE) null else SpilledVariableFieldTypeValue(type, null)
|
||||
|
||||
// INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE,
|
||||
// MULTIANEWARRAY and INVOKEDYNAMIC
|
||||
override fun naryOperation(
|
||||
insn: AbstractInsnNode,
|
||||
values: MutableList<out SpilledVariableFieldTypeValue?>
|
||||
): SpilledVariableFieldTypeValue? {
|
||||
fun updateTypes(argTypes: Array<Type>, withReceiver: Boolean) {
|
||||
val offset = if (withReceiver) 1 else 0
|
||||
for ((index, argType) in argTypes.withIndex()) {
|
||||
val value = values[index + offset] ?: continue
|
||||
if (argType.isIntType()) {
|
||||
value.type = argType
|
||||
} else if (
|
||||
(value.type == AsmTypes.OBJECT_TYPE && argType != AsmTypes.OBJECT_TYPE) ||
|
||||
value.type == NULL_TYPE || value.type == null
|
||||
) {
|
||||
value.type = argType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SpilledVariableFieldTypeValue(
|
||||
when (insn.opcode) {
|
||||
MULTIANEWARRAY -> {
|
||||
Type.getType((insn as MultiANewArrayInsnNode).desc)
|
||||
}
|
||||
INVOKEDYNAMIC -> {
|
||||
updateTypes(Type.getArgumentTypes((insn as InvokeDynamicInsnNode).desc), false)
|
||||
Type.getReturnType(insn.desc)
|
||||
}
|
||||
INVOKESTATIC -> {
|
||||
updateTypes(Type.getArgumentTypes((insn as MethodInsnNode).desc), false)
|
||||
Type.getReturnType(insn.desc)
|
||||
}
|
||||
INVOKEVIRTUAL, INVOKEINTERFACE, INVOKESPECIAL -> {
|
||||
updateTypes(Type.getArgumentTypes((insn as MethodInsnNode).desc), true)
|
||||
Type.getReturnType(insn.desc)
|
||||
}
|
||||
else -> {
|
||||
unreachable(insn)
|
||||
}
|
||||
}, insn
|
||||
)
|
||||
}
|
||||
|
||||
private fun Type.isIntType(): Boolean = when (sort) {
|
||||
Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT, Type.INT -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun unreachable(insn: AbstractInsnNode): Nothing = error("Unreachable instruction ${insn.insnOpcodeText}")
|
||||
|
||||
// IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE
|
||||
override fun ternaryOperation(
|
||||
insn: AbstractInsnNode,
|
||||
arrayref: SpilledVariableFieldTypeValue?,
|
||||
index: SpilledVariableFieldTypeValue?,
|
||||
value: SpilledVariableFieldTypeValue?
|
||||
): SpilledVariableFieldTypeValue? {
|
||||
when (insn.opcode) {
|
||||
IASTORE, LASTORE, FASTORE, DASTORE, AASTORE -> {
|
||||
// nothing to do
|
||||
}
|
||||
BASTORE -> {
|
||||
value?.type = if (arrayref?.type?.descriptor == "[Z") Type.BOOLEAN_TYPE else Type.BYTE_TYPE
|
||||
}
|
||||
CASTORE -> {
|
||||
value?.type = Type.CHAR_TYPE
|
||||
}
|
||||
SASTORE -> {
|
||||
value?.type = Type.SHORT_TYPE
|
||||
}
|
||||
else -> unreachable(insn)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun merge(v: SpilledVariableFieldTypeValue?, w: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? = when {
|
||||
v?.type?.isIntType() == true && w?.type?.isIntType() == true -> v + w
|
||||
v != null && v.type == null -> w
|
||||
w != null && w.type == null -> v
|
||||
v?.type == w?.type -> v
|
||||
else -> SpilledVariableFieldTypeValue(null, v?.insn ?: w?.insn)
|
||||
}
|
||||
|
||||
// IRETURN, LRETURN, FRETURN, DRETURN, ARETURN
|
||||
override fun returnOperation(insn: AbstractInsnNode, value: SpilledVariableFieldTypeValue?, expected: SpilledVariableFieldTypeValue?) {
|
||||
if (insn.opcode == IRETURN) {
|
||||
value?.type = expected?.type
|
||||
}
|
||||
}
|
||||
|
||||
// INEG, LNEG, FNEG, DNEG, IINC, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L,
|
||||
// F2D, D2I, D2L, D2F, I2B, I2C, I2S, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE,
|
||||
// TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN,
|
||||
// PUTSTATIC, GETFIELD, NEWARRAY, ANEWARRAY, ARRAYLENGTH, ATHROW, CHECKCAST,
|
||||
// INSTANCEOF, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? =
|
||||
when (insn.opcode) {
|
||||
INEG, LNEG, FNEG, DNEG, IINC -> SpilledVariableFieldTypeValue(value?.type, insn)
|
||||
I2L, F2L, D2L -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn)
|
||||
I2F, L2F, D2F -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn)
|
||||
L2D, I2D, F2D -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn)
|
||||
L2I, F2I, D2I, ARRAYLENGTH -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn)
|
||||
I2B -> SpilledVariableFieldTypeValue(Type.BYTE_TYPE, insn)
|
||||
I2C -> SpilledVariableFieldTypeValue(Type.CHAR_TYPE, insn)
|
||||
I2S -> SpilledVariableFieldTypeValue(Type.SHORT_TYPE, insn)
|
||||
IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN,
|
||||
ATHROW, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL -> null
|
||||
PUTSTATIC -> {
|
||||
val expectedType = Type.getType((insn as FieldInsnNode).desc)
|
||||
if (expectedType.isIntType()) {
|
||||
value?.type = expectedType
|
||||
}
|
||||
null
|
||||
}
|
||||
GETFIELD -> SpilledVariableFieldTypeValue(Type.getType((insn as FieldInsnNode).desc), insn)
|
||||
NEWARRAY -> when ((insn as IntInsnNode).operand) {
|
||||
T_BOOLEAN -> SpilledVariableFieldTypeValue(Type.getType("[Z"), insn)
|
||||
T_CHAR -> SpilledVariableFieldTypeValue(Type.getType("[C"), insn)
|
||||
T_BYTE -> SpilledVariableFieldTypeValue(Type.getType("[B"), insn)
|
||||
T_SHORT -> SpilledVariableFieldTypeValue(Type.getType("[S"), insn)
|
||||
T_INT -> SpilledVariableFieldTypeValue(Type.getType("[I"), insn)
|
||||
T_FLOAT -> SpilledVariableFieldTypeValue(Type.getType("[F"), insn)
|
||||
T_DOUBLE -> SpilledVariableFieldTypeValue(Type.getType("[D"), insn)
|
||||
T_LONG -> SpilledVariableFieldTypeValue(Type.getType("[J"), insn)
|
||||
else -> unreachable(insn)
|
||||
}
|
||||
ANEWARRAY -> SpilledVariableFieldTypeValue(Type.getType("[${Type.getObjectType((insn as TypeInsnNode).desc)}"), insn)
|
||||
CHECKCAST -> SpilledVariableFieldTypeValue(Type.getObjectType((insn as TypeInsnNode).desc), insn)
|
||||
INSTANCEOF -> SpilledVariableFieldTypeValue(Type.BOOLEAN_TYPE, insn)
|
||||
else -> unreachable(insn)
|
||||
}
|
||||
|
||||
// IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IADD,
|
||||
// LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV,
|
||||
// LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, ISHL, LSHL, ISHR, LSHR, IUSHR,
|
||||
// LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, LCMP, FCMPL, FCMPG, DCMPL,
|
||||
// DCMPG, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE,
|
||||
// IF_ACMPEQ, IF_ACMPNE, PUTFIELD
|
||||
override fun binaryOperation(
|
||||
insn: AbstractInsnNode,
|
||||
v: SpilledVariableFieldTypeValue?,
|
||||
w: SpilledVariableFieldTypeValue?
|
||||
): SpilledVariableFieldTypeValue? =
|
||||
when (insn.opcode) {
|
||||
IALOAD, IADD, ISUB, IMUL, IDIV, IREM, ISHL, ISHR, IUSHR, IAND, IOR, IXOR, LCMP, FCMPL, FCMPG, DCMPL,
|
||||
DCMPG -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn)
|
||||
LALOAD, LADD, LSUB, LMUL, LDIV, LREM, LSHL, LSHR, LUSHR, LAND, LOR, LXOR -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn)
|
||||
FALOAD, FADD, FSUB, FMUL, FDIV, FREM -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn)
|
||||
DALOAD, DADD, DSUB, DMUL, DDIV, DREM -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn)
|
||||
AALOAD -> SpilledVariableFieldTypeValue(AsmTypes.OBJECT_TYPE, insn)
|
||||
BALOAD -> SpilledVariableFieldTypeValue(if (v?.type?.descriptor == "[Z") Type.BOOLEAN_TYPE else Type.BYTE_TYPE, insn)
|
||||
CALOAD -> SpilledVariableFieldTypeValue(Type.CHAR_TYPE, insn)
|
||||
SALOAD -> SpilledVariableFieldTypeValue(Type.SHORT_TYPE, insn)
|
||||
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> null
|
||||
PUTFIELD -> {
|
||||
val expectedType = Type.getType((insn as FieldInsnNode).desc)
|
||||
if (expectedType.isIntType()) {
|
||||
w?.type = expectedType
|
||||
}
|
||||
null
|
||||
}
|
||||
else -> unreachable(insn)
|
||||
}
|
||||
|
||||
// ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE,
|
||||
// ASTORE, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? =
|
||||
when (insn.opcode) {
|
||||
// If same ICONST is stored into several slots, thay can have different types
|
||||
// For example,
|
||||
// val b: Byte = 1
|
||||
// val i: Int = b.toInt()
|
||||
// In this case, `b` and `i` have the same source, but different types.
|
||||
// The example also shows, that the types should be `I`.
|
||||
ISTORE -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn)
|
||||
// Sometimes we cannot get the type from the usage only
|
||||
// For example,
|
||||
// val c = '1'
|
||||
// if (c == '2) ...
|
||||
// In this case, update the type using information from LVT
|
||||
ILOAD -> {
|
||||
methodNode.localVariables.find { local ->
|
||||
local.index == (insn as VarInsnNode).`var` &&
|
||||
methodNode.instructions.indexOf(local.start) < methodNode.instructions.indexOf(insn) &&
|
||||
methodNode.instructions.indexOf(insn) < methodNode.instructions.indexOf(local.end)
|
||||
}?.let { local ->
|
||||
value?.type = Type.getType(local.desc)
|
||||
}
|
||||
value
|
||||
}
|
||||
else -> value
|
||||
}
|
||||
|
||||
// ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4,
|
||||
// ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0,
|
||||
// DCONST_1, BIPUSH, SIPUSH, LDC, JSR, GETSTATIC, NEW
|
||||
override fun newOperation(insn: AbstractInsnNode): SpilledVariableFieldTypeValue? = when (insn.opcode) {
|
||||
ACONST_NULL -> SpilledVariableFieldTypeValue(NULL_TYPE, insn)
|
||||
ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5 -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn)
|
||||
LCONST_0, LCONST_1 -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn)
|
||||
FCONST_0, FCONST_1, FCONST_2 -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn)
|
||||
DCONST_0, DCONST_1 -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn)
|
||||
BIPUSH -> SpilledVariableFieldTypeValue(Type.BYTE_TYPE, insn)
|
||||
SIPUSH -> SpilledVariableFieldTypeValue(Type.SHORT_TYPE, insn)
|
||||
LDC -> when (val cst = (insn as LdcInsnNode).cst) {
|
||||
is Int -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn)
|
||||
is Long -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn)
|
||||
is Float -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn)
|
||||
is Double -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn)
|
||||
is String -> SpilledVariableFieldTypeValue(AsmTypes.JAVA_STRING_TYPE, insn)
|
||||
is Type -> SpilledVariableFieldTypeValue(AsmTypes.JAVA_CLASS_TYPE, insn)
|
||||
else -> SpilledVariableFieldTypeValue(AsmTypes.OBJECT_TYPE, insn)
|
||||
}
|
||||
JSR -> SpilledVariableFieldTypeValue(Type.VOID_TYPE, insn)
|
||||
GETSTATIC -> SpilledVariableFieldTypeValue(Type.getType((insn as FieldInsnNode).desc), insn)
|
||||
NEW -> SpilledVariableFieldTypeValue(Type.getObjectType((insn as TypeInsnNode).desc), insn)
|
||||
else -> unreachable(insn)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun performSpilledVariableFieldTypesAnalysis(
|
||||
methodNode: MethodNode,
|
||||
thisName: String
|
||||
): Array<out Frame<SpilledVariableFieldTypeValue>?> =
|
||||
MethodAnalyzer(thisName, methodNode, SpilledVariableFieldTypesInterpreter(methodNode)).analyze()
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
|
||||
import java.util.*
|
||||
|
||||
// BasicValue interpreter from ASM does not distinct 'int' types from other int-like types like 'byte' or 'boolean',
|
||||
// neither do HotSpot and JVM spec.
|
||||
// But it seems like Dalvik does not follow it, and spilling boolean value into an 'int' field fails with VerifyError on Android 4,
|
||||
// so this function calculates refined frames' markup.
|
||||
// Note that type of some values is only possible to determine by their usages (e.g. ICONST_1, BALOAD both may push boolean or byte on stack)
|
||||
internal fun performRefinedTypeAnalysis(methodNode: MethodNode, thisName: String): Array<out Frame<out BasicValue>?> {
|
||||
val insnList = methodNode.instructions
|
||||
val basicFrames = MethodTransformer.analyze(thisName, methodNode, OptimizationBasicInterpreter())
|
||||
val sourceValueFrames = MethodTransformer.analyze(thisName, methodNode, MySourceInterpreter())
|
||||
|
||||
val expectedTypeAndSourcesByInsnIndex: Array<Pair<Type, List<SourceValue>>?> = arrayOfNulls(insnList.size())
|
||||
|
||||
fun AbstractInsnNode.index() = insnList.indexOf(this)
|
||||
|
||||
fun saveExpectedType(value: SourceValue?, expectedType: Type) {
|
||||
if (value == null) return
|
||||
if (expectedType.sort !in REFINED_INT_SORTS) return
|
||||
|
||||
for (insn in value.insns) {
|
||||
// If source is something like ICONST_0, ignore it
|
||||
if (!insn.isIntLoad()) continue
|
||||
val index = insnList.indexOf(insn)
|
||||
|
||||
checkUpdatedExpectedType(expectedTypeAndSourcesByInsnIndex[index]?.first, expectedType)
|
||||
|
||||
expectedTypeAndSourcesByInsnIndex[index] =
|
||||
Pair(expectedType,
|
||||
expectedTypeAndSourcesByInsnIndex[index]?.second.orEmpty() + value)
|
||||
}
|
||||
}
|
||||
|
||||
fun saveExpectedTypeForArrayStore(insn: AbstractInsnNode, sourceValueFrame: Frame<SourceValue>) {
|
||||
val arrayStoreType =
|
||||
when (insn.opcode) {
|
||||
Opcodes.BASTORE -> Type.BYTE_TYPE
|
||||
Opcodes.CASTORE -> Type.CHAR_TYPE
|
||||
Opcodes.SASTORE -> Type.SHORT_TYPE
|
||||
else -> return
|
||||
}
|
||||
|
||||
val insnIndex = insnList.indexOf(insn)
|
||||
|
||||
val arrayArg = basicFrames[insnIndex].peek(2)
|
||||
// may be different from 'arrayStoreType' in case of boolean arrays (BASTORE opcode is also used for them)
|
||||
val expectedType =
|
||||
if (arrayArg?.type?.sort == Type.ARRAY)
|
||||
arrayArg.type.elementType
|
||||
else
|
||||
arrayStoreType
|
||||
|
||||
saveExpectedType(sourceValueFrame.top(), expectedType)
|
||||
}
|
||||
|
||||
fun saveExpectedTypeForFieldOrMethod(insn: AbstractInsnNode, sourceValueFrame: Frame<SourceValue>) {
|
||||
when (insn.opcode) {
|
||||
Opcodes.PUTFIELD, Opcodes.PUTSTATIC ->
|
||||
saveExpectedType(sourceValueFrame.top(), Type.getType((insn as FieldInsnNode).desc))
|
||||
|
||||
Opcodes.INVOKESTATIC, Opcodes.INVOKEVIRTUAL, Opcodes.INVOKEINTERFACE, Opcodes.INVOKESPECIAL -> {
|
||||
val argumentTypes = Type.getArgumentTypes((insn as MethodInsnNode).desc)
|
||||
argumentTypes.withIndex().forEach {
|
||||
val (argIndex, type) = it
|
||||
saveExpectedType(sourceValueFrame.peek(argumentTypes.size - argIndex - 1), type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveExpectedTypeForVarStore(insn: AbstractInsnNode, sourceValueFrame: Frame<SourceValue>) {
|
||||
if (insn.isIntStore()) {
|
||||
val varIndex = (insn as VarInsnNode).`var`
|
||||
// Considering next insn is important because variable initializer is emitted just before
|
||||
// the beginning of variable
|
||||
val nextInsn = InsnSequence(insn.next, insnList.last).firstOrNull(AbstractInsnNode::isMeaningful)
|
||||
|
||||
val variableNode =
|
||||
methodNode.findContainingVariableFromTable(insn, varIndex)
|
||||
?: methodNode.findContainingVariableFromTable(nextInsn ?: return, varIndex)
|
||||
?: return
|
||||
|
||||
saveExpectedType(sourceValueFrame.top(), Type.getType(variableNode.desc))
|
||||
}
|
||||
}
|
||||
|
||||
for ((insnIndex, insn) in insnList.toArray().withIndex()) {
|
||||
assert(insn.opcode != Opcodes.IRETURN) {
|
||||
"Coroutine body must not contain IRETURN instructions because 'doResume' is always void"
|
||||
}
|
||||
|
||||
val sourceValueFrame = sourceValueFrames[insnIndex] ?: continue
|
||||
|
||||
saveExpectedTypeForArrayStore(insn, sourceValueFrame)
|
||||
saveExpectedTypeForFieldOrMethod(insn, sourceValueFrame)
|
||||
saveExpectedTypeForVarStore(insn, sourceValueFrame)
|
||||
}
|
||||
|
||||
val refinedVarFrames = analyze(methodNode, object : BackwardAnalysisInterpreter<VarExpectedTypeFrame> {
|
||||
override fun newFrame(maxLocals: Int): VarExpectedTypeFrame = VarExpectedTypeFrame(maxLocals)
|
||||
|
||||
override fun def(frame: VarExpectedTypeFrame, insn: AbstractInsnNode) {
|
||||
if (insn.isIntStore()) {
|
||||
frame.expectedTypeByVarIndex[(insn as VarInsnNode).`var`] = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun use(frame: VarExpectedTypeFrame, insn: AbstractInsnNode) {
|
||||
val (expectedType, sources) = expectedTypeAndSourcesByInsnIndex[insn.index()] ?: return
|
||||
|
||||
sources.flatMap(SourceValue::insns).forEach { insnNode ->
|
||||
if (insnNode.isIntLoad()) {
|
||||
frame.updateExpectedType((insnNode as VarInsnNode).`var`, expectedType)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return Array(basicFrames.size) {
|
||||
insnIndex ->
|
||||
val current = Frame(basicFrames[insnIndex] ?: return@Array null)
|
||||
|
||||
refinedVarFrames[insnIndex].expectedTypeByVarIndex.withIndex().filter { it.value != null }.forEach {
|
||||
assert(current.getLocal(it.index)?.type?.sort in ALL_INT_SORTS) {
|
||||
"int type expected, but ${current.getLocal(it.index)?.type} was found in basic frames"
|
||||
}
|
||||
|
||||
current.setLocal(it.index, StrictBasicValue(it.value))
|
||||
}
|
||||
|
||||
current
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isIntLoad() = opcode == Opcodes.ILOAD
|
||||
private fun AbstractInsnNode.isIntStore() = opcode == Opcodes.ISTORE
|
||||
|
||||
private fun checkUpdatedExpectedType(was: Type?, new: Type) {
|
||||
assert(was == null || was == new) {
|
||||
"Conflicting expected types: $was/$new"
|
||||
}
|
||||
}
|
||||
|
||||
private class MySourceInterpreter : SourceInterpreter(Opcodes.API_VERSION) {
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: SourceValue) =
|
||||
when {
|
||||
insn.isStoreOperation() || insn.isLoadOperation() -> SourceValue(value.size, insn)
|
||||
// For DUP* instructions return the same value (effectively ignore DUP's)
|
||||
else -> value
|
||||
}
|
||||
}
|
||||
|
||||
private val REFINED_INT_SORTS = setOf(Type.BOOLEAN, Type.CHAR, Type.BYTE, Type.SHORT)
|
||||
private val ALL_INT_SORTS = REFINED_INT_SORTS + Type.INT
|
||||
|
||||
private fun MethodNode.findContainingVariableFromTable(insn: AbstractInsnNode, varIndex: Int): LocalVariableNode? {
|
||||
val insnIndex = instructions.indexOf(insn)
|
||||
return localVariables.firstOrNull {
|
||||
it.index == varIndex && it.rangeContainsInsn(insnIndex, instructions)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LocalVariableNode.rangeContainsInsn(insnIndex: Int, insnList: InsnList) =
|
||||
insnList.indexOf(start) < insnIndex && insnIndex < insnList.indexOf(end)
|
||||
|
||||
private class VarExpectedTypeFrame(maxLocals: Int) : VarFrame<VarExpectedTypeFrame> {
|
||||
val expectedTypeByVarIndex = arrayOfNulls<Type>(maxLocals)
|
||||
|
||||
override fun mergeFrom(other: VarExpectedTypeFrame) {
|
||||
assert(expectedTypeByVarIndex.size == other.expectedTypeByVarIndex.size) {
|
||||
"Other VarExpectedTypeFrame has different size: ${expectedTypeByVarIndex.size} / ${other.expectedTypeByVarIndex.size}"
|
||||
}
|
||||
|
||||
for ((varIndex, type) in other.expectedTypeByVarIndex.withIndex()) {
|
||||
updateExpectedType(varIndex, type ?: continue)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateExpectedType(varIndex: Int, new: Type) {
|
||||
val was = expectedTypeByVarIndex[varIndex]
|
||||
// Widening to int is always allowed
|
||||
if (new == Type.INT_TYPE) return
|
||||
|
||||
checkUpdatedExpectedType(was, new)
|
||||
|
||||
expectedTypeByVarIndex[varIndex] = new
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || other::class.java != this::class.java) return false
|
||||
|
||||
other as VarExpectedTypeFrame
|
||||
|
||||
if (!Arrays.equals(expectedTypeByVarIndex, other.expectedTypeByVarIndex)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Arrays.hashCode(expectedTypeByVarIndex)
|
||||
}
|
||||
}
|
||||
@@ -8338,6 +8338,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("nullSpilling.kt")
|
||||
public void testNullSpilling_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt", "kotlin.coroutines");
|
||||
|
||||
29
compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt
vendored
Normal file
29
compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
import helpers.*
|
||||
import COROUTINES_PACKAGE.*
|
||||
import COROUTINES_PACKAGE.intrinsics.*
|
||||
|
||||
fun foo() {
|
||||
bar {
|
||||
val p = false
|
||||
baz(p, "".ifEmpty { "OK" })
|
||||
}
|
||||
}
|
||||
|
||||
var res = "FAIL"
|
||||
|
||||
fun bar(f: suspend () -> Unit) {
|
||||
f.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
fun baz(p: Boolean, s: String?) {
|
||||
res = s!!
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
foo()
|
||||
return res
|
||||
}
|
||||
49
compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt
vendored
Normal file
49
compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// FILE: test.kt
|
||||
// COMMON_COROUTINES_TEST
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// WITH_REFLECT
|
||||
|
||||
import COROUTINES_PACKAGE.*
|
||||
import helpers.*
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
interface SuspendRunnable {
|
||||
suspend fun run(): String
|
||||
}
|
||||
|
||||
suspend inline fun test(crossinline c: suspend (String) -> String): String {
|
||||
val sr = object : SuspendRunnable {
|
||||
val ok by Delegate()
|
||||
override suspend fun run(): String {
|
||||
return c(ok)
|
||||
}
|
||||
}
|
||||
return sr.run()
|
||||
}
|
||||
|
||||
// FILE: box.kt
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
import COROUTINES_PACKAGE.*
|
||||
import helpers.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
suspend fun dummy() {}
|
||||
|
||||
fun box(): String {
|
||||
var res: String = "FAIL"
|
||||
builder {
|
||||
res = test { it }
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -9553,6 +9553,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("nullSpilling.kt")
|
||||
public void testNullSpilling_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt", "kotlin.coroutines.experimental");
|
||||
|
||||
@@ -3808,6 +3808,16 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleRegenerationWithNonSuspendingLambda.kt")
|
||||
public void testDoubleRegenerationWithNonSuspendingLambda_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/doubleRegenerationWithNonSuspendingLambda.kt", "kotlin.coroutines.experimental");
|
||||
|
||||
@@ -3808,6 +3808,16 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleRegenerationWithNonSuspendingLambda.kt")
|
||||
public void testDoubleRegenerationWithNonSuspendingLambda_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/doubleRegenerationWithNonSuspendingLambda.kt", "kotlin.coroutines.experimental");
|
||||
|
||||
@@ -9553,6 +9553,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("nullSpilling.kt")
|
||||
public void testNullSpilling_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt", "kotlin.coroutines.experimental");
|
||||
|
||||
@@ -8338,6 +8338,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("nullSpilling.kt")
|
||||
public void testNullSpilling_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -3798,6 +3798,11 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleRegenerationWithNonSuspendingLambda.kt")
|
||||
public void testDoubleRegenerationWithNonSuspendingLambda_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/doubleRegenerationWithNonSuspendingLambda.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -3798,6 +3798,11 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleRegenerationWithNonSuspendingLambda.kt")
|
||||
public void testDoubleRegenerationWithNonSuspendingLambda_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/doubleRegenerationWithNonSuspendingLambda.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -7063,6 +7063,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("nullSpilling.kt")
|
||||
public void testNullSpilling_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -3388,6 +3388,11 @@ public class IrJsCodegenInlineES6TestGenerated extends AbstractIrJsCodegenInline
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleRegenerationWithNonSuspendingLambda.kt")
|
||||
public void testDoubleRegenerationWithNonSuspendingLambda_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/doubleRegenerationWithNonSuspendingLambda.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -7073,6 +7073,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("nullSpilling.kt")
|
||||
public void testNullSpilling_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -3388,6 +3388,11 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleRegenerationWithNonSuspendingLambda.kt")
|
||||
public void testDoubleRegenerationWithNonSuspendingLambda_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/doubleRegenerationWithNonSuspendingLambda.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -7073,6 +7073,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt19475.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("kt38925.kt")
|
||||
public void testKt38925_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/kt38925.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("nullSpilling.kt")
|
||||
public void testNullSpilling_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/varSpilling/nullSpilling.kt", "kotlin.coroutines");
|
||||
|
||||
@@ -3388,6 +3388,11 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedProperties.kt")
|
||||
public void testDelegatedProperties_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/delegatedProperties.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleRegenerationWithNonSuspendingLambda.kt")
|
||||
public void testDoubleRegenerationWithNonSuspendingLambda_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/doubleRegenerationWithNonSuspendingLambda.kt", "kotlin.coroutines");
|
||||
|
||||
Reference in New Issue
Block a user