diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index abde93d3c26..55217f16899 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1919,11 +1919,6 @@ public class ExpressionCodegen extends KtVisitor impleme return asmType(varType); } - private static boolean isSharedVarType(@NotNull Type type) { - return type.getSort() == Type.OBJECT && type.getInternalName().startsWith(REF_TYPE_PREFIX); - } - - private void putDescriptorIntoFrameMap(@NotNull KtElement statement) { if (statement instanceof KtDestructuringDeclaration) { KtDestructuringDeclaration multiDeclaration = (KtDestructuringDeclaration) statement; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 1c00e4114ee..05e103b86dc 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -1359,9 +1359,7 @@ public abstract class StackValue { default: PrimitiveType primitiveType = AsmUtil.asmPrimitiveTypeToLangPrimitiveType(type); if (primitiveType == null) throw new UnsupportedOperationException(); - - String typeName = primitiveType.getTypeName().getIdentifier(); - return Type.getObjectType(REF_TYPE_PREFIX + typeName + "Ref"); + return sharedTypeForPrimitive(primitiveType); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java index 96a37c897e6..1e901b4c5ea 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.codegen.TransformationMethodVisitor; import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer; import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantCoercionToUnitTransformer; +import org.jetbrains.kotlin.codegen.optimization.captured.CapturedVarsOptimizationMethodTransformer; import org.jetbrains.kotlin.codegen.optimization.common.UtilKt; import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer; import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer; @@ -33,6 +34,7 @@ public class OptimizationMethodVisitor extends TransformationMethodVisitor { private static final MethodTransformer MANDATORY_METHOD_TRANSFORMER = new FixStackWithLabelNormalizationMethodTransformer(); private static final MethodTransformer[] OPTIMIZATION_TRANSFORMERS = new MethodTransformer[] { + new CapturedVarsOptimizationMethodTransformer(), new RedundantNullCheckMethodTransformer(), new RedundantBoxingMethodTransformer(), new RedundantCoercionToUnitTransformer(), diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java index 6b4e65dc088..afff36f18a0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java @@ -21,6 +21,7 @@ import kotlin.collections.CollectionsKt; import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue; +import org.jetbrains.kotlin.codegen.optimization.common.UtilKt; import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer; import org.jetbrains.org.objectweb.asm.Opcodes; import org.jetbrains.org.objectweb.asm.Type; @@ -48,7 +49,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { adaptLocalVariableTableForBoxedValues(node, frames); - applyVariablesRemapping(node, buildVariablesRemapping(valuesToOptimize, node)); + UtilKt.remapLocalVariables(node, buildVariablesRemapping(valuesToOptimize, node)); adaptInstructionsForBoxedValues(node, valuesToOptimize); } @@ -221,21 +222,6 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { return remapping; } - private static void applyVariablesRemapping(@NotNull MethodNode node, @NotNull int[] remapping) { - for (AbstractInsnNode insn : node.instructions.toArray()) { - if (insn instanceof VarInsnNode) { - ((VarInsnNode) insn).var = remapping[((VarInsnNode) insn).var]; - } - if (insn instanceof IincInsnNode) { - ((IincInsnNode) insn).var = remapping[((IincInsnNode) insn).var]; - } - } - - for (LocalVariableNode localVariableNode : node.localVariables) { - localVariableNode.index = remapping[localVariableNode.index]; - } - } - private static void adaptInstructionsForBoxedValues( @NotNull MethodNode node, @NotNull RedundantBoxedValuesCollection values diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/CapturedVarsOptimizationMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/CapturedVarsOptimizationMethodTransformer.kt new file mode 100644 index 00000000000..a6194d3bdd0 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/CapturedVarsOptimizationMethodTransformer.kt @@ -0,0 +1,264 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.optimization.captured + +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks +import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables +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.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +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 + +class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { + override fun transform(internalClassName: String, methodNode: MethodNode) { + Transformer(internalClassName, methodNode).run() + } + + // Tracks proper usages of objects corresponding to captured variables. + // + // The 'kotlin.jvm.internal.Ref.*' instance can be replaced with a local variable, + // if all of the following conditions are satisfied: + // * It is created inside a current method. + // * The only permitted operations on it are: + // - store to a local variable + // - ALOAD, ASTORE + // - DUP, POP + // - GETFIELD .element, PUTFIELD .element + // * There's a corresponding local variable definition, + // and all ALOAD/ASTORE instructions operate on that particular local variable. + // * Its 'element' field is initialized at start of local variable visibility range. + // + // Note that for code that doesn't create Ref objects explicitly these conditions are true, + // unless the Ref object escapes to a local class constructor (including local classes for lambdas). + // + private class CapturedVarDescriptor(val newInsn: TypeInsnNode, val refType: Type, val valueType: Type) : ReferenceValueDescriptor { + var hazard = false + + var initCallInsn: MethodInsnNode? = null + var localVar: LocalVariableNode? = null + var localVarIndex = -1 + val astoreInsns: MutableCollection = LinkedHashSet() + val aloadInsns: MutableCollection = LinkedHashSet() + val stackInsns: MutableCollection = LinkedHashSet() + val getFieldInsns: MutableCollection = LinkedHashSet() + val putFieldInsns: MutableCollection = LinkedHashSet() + + fun canRewrite(): Boolean = + !hazard && + initCallInsn != null && + localVar != null && + localVarIndex >= 0 + + override fun onUseAsTainted() { + hazard = true + } + } + + private class Transformer(private val internalClassName: String, private val methodNode: MethodNode) { + private val refValues = ArrayList() + private val refValuesByNewInsn = LinkedHashMap() + private val insns = methodNode.instructions.toArray() + private lateinit var frames: Array?> + + val hasRewritableRefValues: Boolean + get() = refValues.isNotEmpty() + + fun run() { + createRefValues() + if (!hasRewritableRefValues) return + + analyze() + if (!hasRewritableRefValues) return + + rewrite() + } + + private fun AbstractInsnNode.getIndex() = methodNode.instructions.indexOf(this) + + private fun createRefValues() { + for (insn in insns) { + if (insn.opcode == Opcodes.NEW && insn is TypeInsnNode) { + val type = Type.getObjectType(insn.desc) + if (AsmTypes.isSharedVarType(type)) { + val valueType = REF_TYPE_TO_ELEMENT_TYPE[type.internalName] ?: continue + val refValue = CapturedVarDescriptor(insn, type, valueType) + refValues.add(refValue) + refValuesByNewInsn[insn] = refValue + } + } + } + } + + private inner class Interpreter : ReferenceTrackingInterpreter() { + override fun newOperation(insn: AbstractInsnNode): BasicValue = + refValuesByNewInsn[insn]?.let { descriptor -> + ProperTrackedReferenceValue(descriptor.refType, descriptor) + } + ?: super.newOperation(insn) + + override fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) { + for (descriptor in value.descriptors) { + if (descriptor !is CapturedVarDescriptor) throw AssertionError("Unexpected descriptor: $descriptor") + when { + insn.opcode == Opcodes.ALOAD -> + descriptor.aloadInsns.add(insn as VarInsnNode) + insn.opcode == Opcodes.ASTORE -> + descriptor.astoreInsns.add(insn as VarInsnNode) + insn.opcode == Opcodes.GETFIELD && insn is FieldInsnNode && insn.name == REF_ELEMENT_FIELD && position == 0 -> + descriptor.getFieldInsns.add(insn) + insn.opcode == Opcodes.PUTFIELD && insn is FieldInsnNode && insn.name == REF_ELEMENT_FIELD && position == 0 -> + descriptor.putFieldInsns.add(insn) + insn.opcode == Opcodes.INVOKESPECIAL && insn is MethodInsnNode && insn.name == INIT_METHOD_NAME && position == 0 -> + if (descriptor.initCallInsn != null && descriptor.initCallInsn != insn) + descriptor.hazard = true + else + descriptor.initCallInsn = insn + insn.opcode == Opcodes.DUP -> + descriptor.stackInsns.add(insn) + else -> + descriptor.hazard = true + } + } + } + + } + + private fun analyze() { + frames = MethodTransformer.analyze(internalClassName, methodNode, Interpreter()) + trackPops() + assignLocalVars() + + refValues.removeAll { !it.canRewrite() } + } + + private fun trackPops() { + for (i in insns.indices) { + val frame = frames[i] ?: continue + val insn = insns[i] + + when (insn.opcode) { + Opcodes.POP -> { + frame.top()?.getCapturedVarOrNull()?.run { stackInsns.add(insn) } + } + Opcodes.POP2 -> { + val top = frame.top() + if (top?.size == 1) { + top.getCapturedVarOrNull()?.hazard = true + frame.peek(1)?.getCapturedVarOrNull()?.hazard = true + } + } + } + } + } + + private fun BasicValue.getCapturedVarOrNull() = + safeAs()?.descriptor?.safeAs() + + private fun assignLocalVars() { + for (localVar in methodNode.localVariables) { + val type = Type.getType(localVar.desc) + if (!AsmTypes.isSharedVarType(type)) continue + + val startFrame = frames[localVar.start.getIndex()] ?: continue + + val refValue = startFrame.getLocal(localVar.index) as? ProperTrackedReferenceValue ?: continue + val descriptor = refValue.descriptor as? CapturedVarDescriptor ?: continue + + if (descriptor.hazard) continue + + if (descriptor.localVar == null) { + descriptor.localVar = localVar + } + else { + descriptor.hazard = true + } + } + + for (refValue in refValues) { + if (refValue.hazard) continue + val localVar = refValue.localVar ?: continue + + if (refValue.valueType.size != 1) { + refValue.localVarIndex = methodNode.maxLocals + methodNode.maxLocals += 2 + localVar.index = refValue.localVarIndex + } + else { + refValue.localVarIndex = localVar.index + } + + val startIndex = localVar.start.getIndex() + val initFieldInsns = refValue.putFieldInsns.filter { it.getIndex() < startIndex } + if (initFieldInsns.size != 1) { + refValue.hazard = true + continue + } + } + } + + private fun rewrite() { + for (refValue in refValues) { + if (!refValue.canRewrite()) continue + + rewriteRefValue(refValue) + } + + methodNode.removeEmptyCatchBlocks() + methodNode.removeUnusedLocalVariables() + } + + private fun rewriteRefValue(capturedVar: CapturedVarDescriptor) { + methodNode.instructions.run { + capturedVar.localVar!!.let { + it.signature = null + it.desc = capturedVar.valueType.descriptor + } + + remove(capturedVar.newInsn) + remove(capturedVar.initCallInsn!!) + capturedVar.stackInsns.forEach { remove(it) } + capturedVar.aloadInsns.forEach { remove(it) } + capturedVar.astoreInsns.forEach { remove(it) } + + capturedVar.getFieldInsns.forEach { + set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ILOAD), capturedVar.localVarIndex)) + } + + capturedVar.putFieldInsns.forEach { + set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex)) + } + } + } + } +} + +internal const val REF_ELEMENT_FIELD = "element" +internal const val INIT_METHOD_NAME = "" + +internal val REF_TYPE_TO_ELEMENT_TYPE = HashMap().apply { + put(AsmTypes.OBJECT_REF_TYPE.internalName, AsmTypes.OBJECT_TYPE) + PrimitiveType.values().forEach { + put(AsmTypes.sharedTypeForPrimitive(it).internalName, AsmTypes.valueTypeForPrimitive(it)) + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/ReferenceTrackingInterpreter.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/ReferenceTrackingInterpreter.kt new file mode 100644 index 00000000000..c41ee4cf1c8 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/ReferenceTrackingInterpreter.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.optimization.captured + +import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter +import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue + +abstract class ReferenceTrackingInterpreter : OptimizationBasicInterpreter() { + override fun merge(v: BasicValue, w: BasicValue): BasicValue = + when { + v is ProperTrackedReferenceValue && w is ProperTrackedReferenceValue -> + if (v.descriptor == w.descriptor) + v + else + TaintedTrackedReferenceValue( + getTaintedValueType(v.type, w.type), + setOf(v.descriptor, w.descriptor) + ) + + v is TrackedReferenceValue || w is TrackedReferenceValue -> + TaintedTrackedReferenceValue( + getTaintedValueType(v.type, w.type), + v.referenceValueDescriptors + w.referenceValueDescriptors + ) + + else -> + super.merge(v, w) + } + + private val BasicValue.referenceValueDescriptors: Set + get() = if (this is TrackedReferenceValue) this.descriptors else emptySet() + + private fun getTaintedValueType(type1: Type?, type2: Type?): Type = + when { + type1 == null || type2 == null -> AsmTypes.OBJECT_TYPE + type1 == type2 -> type1 + else -> AsmTypes.OBJECT_TYPE + } + + override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? = + if (value is TrackedReferenceValue) { + checkRefValuesUsages(insn, listOf(value)) + value + } + else { + super.copyOperation(insn, value) + } + + override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? { + checkRefValuesUsages(insn, listOf(value)) + return super.unaryOperation(insn, value) + } + + override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? { + checkRefValuesUsages(insn, listOf(value1, value2)) + return super.binaryOperation(insn, value1, value2) + } + + override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? { + checkRefValuesUsages(insn, listOf(value1, value2, value3)) + return super.ternaryOperation(insn, value1, value2, value3) + } + + override fun naryOperation(insn: AbstractInsnNode, values: List): BasicValue? { + checkRefValuesUsages(insn, values) + return super.naryOperation(insn, values) + } + + protected open fun checkRefValuesUsages(insn: AbstractInsnNode, values: List) { + values.forEach { value -> + if (value is TaintedTrackedReferenceValue) { + value.descriptors.forEach { it.onUseAsTainted() } + } + } + + values.forEachIndexed { pos, value -> + if (value is TrackedReferenceValue) { + processRefValueUsage(value, insn, pos) + } + } + } + + protected abstract fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) +} + diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/TrackedReferenceValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/TrackedReferenceValue.kt new file mode 100644 index 00000000000..51a975053c1 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/TrackedReferenceValue.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.optimization.captured + +import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue +import org.jetbrains.org.objectweb.asm.Type + +interface ReferenceValueDescriptor { + fun onUseAsTainted() +} + +abstract class TrackedReferenceValue(type: Type): StrictBasicValue(type) { + abstract val descriptors: Set +} + +class ProperTrackedReferenceValue(type: Type, val descriptor: ReferenceValueDescriptor) : TrackedReferenceValue(type) { + override val descriptors: Set + get() = setOf(descriptor) + + override fun equals(other: Any?): Boolean = + other === this || + other is ProperTrackedReferenceValue && other.descriptor == this.descriptor + + override fun hashCode(): Int = + descriptor.hashCode() + + override fun toString(): String = + "[$descriptor]" +} + +class TaintedTrackedReferenceValue(type: Type, override val descriptors: Set) : TrackedReferenceValue(type) { + override fun equals(other: Any?): Boolean = + other === this || + other is TaintedTrackedReferenceValue && other.descriptors == this.descriptors + + override fun hashCode(): Int = + descriptors.hashCode() + + override fun toString(): String = + "!$descriptors" +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt index 2dc04c11880..4832c387d39 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt @@ -17,8 +17,10 @@ package org.jetbrains.kotlin.codegen.optimization.common import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil +import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes.* +import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.* val AbstractInsnNode.isMeaningful: Boolean get() = @@ -77,6 +79,62 @@ fun MethodNode.removeEmptyCatchBlocks() { } } +fun MethodNode.removeUnusedLocalVariables() { + val used = BooleanArray(maxLocals) { false } + for (insn in instructions) { + when (insn) { + is VarInsnNode -> { + val varIndex = insn.`var` + used[varIndex] = true + if (insn.isSize2LoadStoreOperation()) { + used[varIndex + 1] = true + } + } + is IincInsnNode -> + used[insn.`var`] = true + } + } + for (localVar in localVariables) { + val varIndex = localVar.index + used[varIndex] = true + val type = Type.getType(localVar.desc) + if (type.size == 2) { + used[varIndex + 1] = true + } + } + + if (used.all { it }) return + + val remapping = IntArray(maxLocals) { 0 } + var lastUnused = 0 + for (i in remapping.indices) { + remapping[i] = lastUnused + if (used[i]) { + lastUnused++ + } + } + + remapLocalVariables(remapping) +} + +private fun VarInsnNode.isSize2LoadStoreOperation() = + opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE + +fun MethodNode.remapLocalVariables(remapping: IntArray) { + for (insn in instructions.toArray()) { + when (insn) { + is VarInsnNode -> + insn.`var` = remapping[insn.`var`] + is IincInsnNode -> + insn.`var` = remapping[insn.`var`] + } + } + + for (localVariableNode in localVariables) { + localVariableNode.index = remapping[localVariableNode.index] + } +} + inline fun AbstractInsnNode.findNextOrNull(predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? { var finger = this.next while (finger != null && !predicate(finger)) { @@ -125,3 +183,10 @@ fun AbstractInsnNode.isLoadOperation(): Boolean = getOpcode() in Opcodes.ILOAD.. val AbstractInsnNode?.insnText get() = InlineCodegenUtil.getInsnText(this) val AbstractInsnNode?.debugText get() = if (this == null) "" else "${this.javaClass.simpleName}: $insnText" + +internal inline fun AbstractInsnNode.isInsn(opcode: Int, condition: T.() -> Boolean): Boolean = + takeInsnIf(opcode, condition) != null + +internal inline fun AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? = + takeIf { it.opcode == opcode }?.safeAs()?.takeIf { it.condition() } + diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java index add99a73509..9a4b3b8d760 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.resolve.jvm; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.builtins.PrimitiveType; import org.jetbrains.org.objectweb.asm.Type; import java.util.HashMap; @@ -83,6 +84,40 @@ public class AsmTypes { return Type.getObjectType("kotlin/reflect/" + className); } + public static boolean isSharedVarType(@NotNull Type type) { + return type.getSort() == Type.OBJECT && type.getInternalName().startsWith(REF_TYPE_PREFIX); + } + + @NotNull + public static Type sharedTypeForPrimitive(@NotNull PrimitiveType primitiveType) { + String typeName = primitiveType.getTypeName().getIdentifier(); + return Type.getObjectType(REF_TYPE_PREFIX + typeName + "Ref"); + } + + @NotNull + public static Type valueTypeForPrimitive(PrimitiveType primitiveType) { + switch (primitiveType) { + case BOOLEAN: + return Type.BOOLEAN_TYPE; + case CHAR: + return Type.CHAR_TYPE; + case BYTE: + return Type.BYTE_TYPE; + case SHORT: + return Type.SHORT_TYPE; + case INT: + return Type.INT_TYPE; + case FLOAT: + return Type.FLOAT_TYPE; + case LONG: + return Type.LONG_TYPE; + case DOUBLE: + return Type.DOUBLE_TYPE; + default: + throw new UnsupportedOperationException(); + } + } + @NotNull public static Type getType(@NotNull Class javaClass) { Type type = TYPES_MAP.get(javaClass); diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt new file mode 100644 index 00000000000..0568c83dde4 --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt @@ -0,0 +1,9 @@ +inline fun runCrossInline(crossinline f: () -> Unit) { + f() +} + +fun box(): String { + var x = "" + runCrossInline { x = "OK" } + return x +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt new file mode 100644 index 00000000000..5faf6875838 --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun box(): String { + var x = "" + run { x = "OK" } + return x +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt new file mode 100644 index 00000000000..0f62af3ed4d --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME + +fun box(): String { + var x = "" + run { + x += "O" + x += "K" + } + return x +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt new file mode 100644 index 00000000000..c8d5c31083e --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME + +fun box(): String { + var x = 0 + run { x++ } + run { ++x } + return if (x == 2) "OK" else "Fail: $x" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt new file mode 100644 index 00000000000..44e0531c7b7 --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt @@ -0,0 +1,18 @@ +// WITH_RUNTIME + +class Host(var value: String) { + operator fun get(i: Int, j: Int, k: Int) = value + + operator fun set(i: Int, j: Int, k: Int, newValue: String) { + value = newValue + } +} + +fun box(): String { + var x = Host("") + run { + x[0, 0, 0] += "O" + x[0, 0, 0] += "K" + } + return x.value +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt new file mode 100644 index 00000000000..10f31cbc43a --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt @@ -0,0 +1,26 @@ +// WITH_RUNTIME + +fun box(): String { + var xl = 0L // Long, size 2 + var xi = 0 // Int, size 1 + var xd = 0.0 // Double, size 2 + + run { + xl++ + xd += 1.0 + xi++ + } + + run { + run { + xl++ + xd += 1.0 + xi++ + } + } + + if (xi != 2) return "fail: xi=$xi" + if (xl != 2L) return "fail: xl=$xl" + if (xd != 2.0) return "fail: xd=$xd" + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt new file mode 100644 index 00000000000..09ed429ede5 --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt @@ -0,0 +1,19 @@ +// WITH_RUNTIME + +fun box(): String { + run { + run { + var x = 0 + run { ++x } + if (x == 0) return "fail" + } + + run { + var x = 0 + run { x++ } + if (x == 0) return "fail" + } + } + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt new file mode 100644 index 00000000000..c1bbf1a27d9 --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt @@ -0,0 +1,41 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + + +class Controller { + var result = "" + + suspend fun suspendWithResult(value: T): T = suspendCoroutineOrReturn { c -> + c.resume(value) + COROUTINE_SUSPENDED + } +} + +fun builder(c: suspend Controller.() -> Unit): String { + val controller = Controller() + c.startCoroutine(controller, EmptyContinuation) + return controller.result +} + +fun box(): String { + val value = builder { + var r = "" + + for (x in listOf("O", "$", "K")) { + if (x == "$") continue + run { + r += suspendWithResult(x) + } + } + run { + r += "." + } + result = r + } + + if (value != "OK.") return "fail: suspend in for body: $value" + + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt new file mode 100644 index 00000000000..2d1e4d88113 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME + +fun test() { + var x = 0 + run { + run { + run { + ++x + } + } + } +} + +// 0 NEW +// 0 GETFIELD +// 0 PUTFIELD \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt new file mode 100644 index 00000000000..c0d87f2e430 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME + +fun test() { + var x = 0 + run { ++x } +} + +// 0 NEW +// 0 GETFIELD +// 0 PUTFIELD \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInLocalObject.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInLocalObject.kt new file mode 100644 index 00000000000..b12531d62b1 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInLocalObject.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +fun test() { + var x = 0 + run { + val obj = object { + fun foo() { ++x } + } + obj.foo() + } +} + +// 1 NEW kotlin/jvm/internal/Ref\$IntRef +// 2 GETFIELD kotlin/jvm/internal/Ref\$IntRef\.element +// 2 PUTFIELD kotlin/jvm/internal/Ref\$IntRef\.element \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlineOnly.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlineOnly.kt new file mode 100644 index 00000000000..7537f21d76e --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlineOnly.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME + +fun runNoInline(f: () -> Unit) = f() + +fun test() { + var x = 0 + runNoInline { ++x } +} + +// 1 NEW kotlin/jvm/internal/Ref\$IntRef +// 2 GETFIELD kotlin/jvm/internal/Ref\$IntRef\.element +// 2 PUTFIELD kotlin/jvm/internal/Ref\$IntRef\.element \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlneInsideChainOfInlineFuns.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlneInsideChainOfInlineFuns.kt new file mode 100644 index 00000000000..3ca3d3887cd --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlneInsideChainOfInlineFuns.kt @@ -0,0 +1,20 @@ +// WITH_RUNTIME + +fun runNoInline(f: () -> Unit) = f() + +fun test() { + var x = 0 + run { + run { + runNoInline { + run { + ++x + } + } + } + } +} + +// 1 NEW kotlin/jvm/internal/Ref\$IntRef +// 2 GETFIELD kotlin/jvm/internal/Ref\$IntRef\.element +// 2 PUTFIELD kotlin/jvm/internal/Ref\$IntRef\.element \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt new file mode 100644 index 00000000000..612779bc861 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME + +fun box(): String { + var xl = 0L // Long, size 2 + var xi = 0 // Int, size 1 + var xd = 0.0 // Double, size 2 + + run { + xl++ + xd += 1.0 + xi++ + } + + run { + run { + xl++ + xd += 1.0 + xi++ + } + } + + return "OK" +} + +// 0 NEW \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt new file mode 100644 index 00000000000..acbfaf5acc8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt @@ -0,0 +1,32 @@ +// WITH_RUNTIME + +fun box(): String { + run { + run { + var x1 = 0 + run { ++x1 } + if (x1 == 0) return "fail" + } + + run { + var x2 = 0 + run { x2++ } + if (x2 == 0) return "fail" + } + } + + return "OK" +} + + +// Shared variable slots (x1, x2): +// 5 ILOAD 0 +// 4 ISTORE 0 + +// Temporary variable slots for 'x2++': +// 1 ILOAD 1 +// 1 ISTORE 1 + +// 0 NEW +// 0 GETFIELD +// 0 PUTFIELD \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt new file mode 100644 index 00000000000..e42c6440663 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +fun add(x: Int, y: Int) = x + y + +fun test() { + var x = 0 + run { + x += add(1, try { 1 } catch (e: Throwable) { 42 }) + } +} + +// 0 NEW +// 0 GETFIELD +// 0 PUTFIELD \ No newline at end of file diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInCrossinline.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInCrossinline.txt new file mode 100644 index 00000000000..7e201ed9d14 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInCrossinline.txt @@ -0,0 +1,5 @@ +@kotlin.Metadata +public final class CapturedInCrossinlineKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method runCrossInline(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0): void +} diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.txt new file mode 100644 index 00000000000..bc00058e364 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.txt @@ -0,0 +1,4 @@ +@kotlin.Metadata +public final class CapturedInInlineOnlyAssignKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.txt new file mode 100644 index 00000000000..0f9a144a008 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.txt @@ -0,0 +1,4 @@ +@kotlin.Metadata +public final class CapturedInInlineOnlyCAOKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.txt new file mode 100644 index 00000000000..12471cbac06 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.txt @@ -0,0 +1,4 @@ +@kotlin.Metadata +public final class CapturedInInlineOnlyIncrDecrKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.txt new file mode 100644 index 00000000000..ff746875374 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.txt @@ -0,0 +1,14 @@ +@kotlin.Metadata +public final class CapturedInInlineOnlyIndexedCAOKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public final class Host { + private @org.jetbrains.annotations.NotNull field value: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method get(p0: int, p1: int, p2: int): java.lang.String + public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String + public final method set(p0: int, p1: int, p2: int, @org.jetbrains.annotations.NotNull p3: java.lang.String): void + public final method setValue(@org.jetbrains.annotations.NotNull p0: java.lang.String): void +} diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedVarsOfSize2.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedVarsOfSize2.txt new file mode 100644 index 00000000000..c8379afb4c9 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/capturedVarsOfSize2.txt @@ -0,0 +1,4 @@ +@kotlin.Metadata +public final class CapturedVarsOfSize2Kt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.txt new file mode 100644 index 00000000000..44c76b70cce --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.txt @@ -0,0 +1,4 @@ +@kotlin.Metadata +public final class SharedSlotsWithCapturedVarsKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/withCoroutines.txt b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/withCoroutines.txt new file mode 100644 index 00000000000..2fe0c0856b1 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/closures/capturedVarsOptimization/withCoroutines.txt @@ -0,0 +1,39 @@ +@kotlin.Metadata +public final class Controller { + private @org.jetbrains.annotations.NotNull field result: java.lang.String + public method (): void + public final @org.jetbrains.annotations.NotNull method getResult(): java.lang.String + public final method setResult(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.Nullable method suspendWithResult(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation +} + +@kotlin.Metadata +public class EmptyContinuation { + public final static field Companion: EmptyContinuation.Companion + private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.experimental.CoroutineContext + inner class EmptyContinuation/Companion + public @synthetic.kotlin.jvm.GeneratedByJvmOverloads method (): void + public method (@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.CoroutineContext): void + public synthetic method (p0: kotlin.coroutines.experimental.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void + public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.experimental.CoroutineContext + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final static class EmptyContinuation/Companion { + inner class EmptyContinuation/Companion + private method (): void +} + +@kotlin.Metadata +public final class WithCoroutinesKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): java.lang.String +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 30659cdc68c..51a21bcb6db 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -3767,6 +3767,63 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CapturedVarsOptimization extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInCapturedVarsOptimization() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("capturedInCrossinline.kt") + public void testCapturedInCrossinline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyAssign.kt") + public void testCapturedInInlineOnlyAssign() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyCAO.kt") + public void testCapturedInInlineOnlyCAO() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyIncrDecr.kt") + public void testCapturedInInlineOnlyIncrDecr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyIndexedCAO.kt") + public void testCapturedInInlineOnlyIndexedCAO() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt"); + doTest(fileName); + } + + @TestMetadata("capturedVarsOfSize2.kt") + public void testCapturedVarsOfSize2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt"); + doTest(fileName); + } + + @TestMetadata("sharedSlotsWithCapturedVars.kt") + public void testSharedSlotsWithCapturedVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt"); + doTest(fileName); + } + + @TestMetadata("withCoroutines.kt") + public void testWithCoroutines() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 2c8fa254c22..ceec6430086 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -3767,6 +3767,63 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CapturedVarsOptimization extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInCapturedVarsOptimization() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("capturedInCrossinline.kt") + public void testCapturedInCrossinline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyAssign.kt") + public void testCapturedInInlineOnlyAssign() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyCAO.kt") + public void testCapturedInInlineOnlyCAO() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyIncrDecr.kt") + public void testCapturedInInlineOnlyIncrDecr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyIndexedCAO.kt") + public void testCapturedInInlineOnlyIndexedCAO() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt"); + doTest(fileName); + } + + @TestMetadata("capturedVarsOfSize2.kt") + public void testCapturedVarsOfSize2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt"); + doTest(fileName); + } + + @TestMetadata("sharedSlotsWithCapturedVars.kt") + public void testSharedSlotsWithCapturedVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt"); + doTest(fileName); + } + + @TestMetadata("withCoroutines.kt") + public void testWithCoroutines() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 753565a7adf..b7952e69c67 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -648,6 +648,63 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CapturedVarsOptimization extends AbstractBytecodeTextTest { + public void testAllFilesPresentInCapturedVarsOptimization() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("capturedInChainOfInlineFuns.kt") + public void testCapturedInChainOfInlineFuns() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnly.kt") + public void testCapturedInInlineOnly() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInLocalObject.kt") + public void testCapturedInLocalObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInLocalObject.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInNoInlineOnly.kt") + public void testCapturedInNoInlineOnly() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlineOnly.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInNoInlneInsideChainOfInlineFuns.kt") + public void testCapturedInNoInlneInsideChainOfInlineFuns() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInNoInlneInsideChainOfInlineFuns.kt"); + doTest(fileName); + } + + @TestMetadata("capturedVarsOfSize2.kt") + public void testCapturedVarsOfSize2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt"); + doTest(fileName); + } + + @TestMetadata("sharedSlotsWithCapturedVars.kt") + public void testSharedSlotsWithCapturedVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt"); + doTest(fileName); + } + + @TestMetadata("withStackNormalization.kt") + public void testWithStackNormalization() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/checkcast") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out b/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out index 8cdb83b828a..9675d4d5032 100644 --- a/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out +++ b/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out @@ -23,8 +23,7 @@ inline fun foo(f: () -> Unit) { // RESULT: 1: I frame = main:7, FrameSharedVarLocalVarKt {frameSharedVarLocalVar} local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameSharedVarLocalVar.kt, 3) - local = var1: kotlin.jvm.internal.Ref$IntRef = {kotlin.jvm.internal.Ref$IntRef@uniqueID}1 (sp = frameSharedVarLocalVar.kt, 4) - field = element: int = 1 (sp = Ref.!EXT!) + local = var1: int = 1 (sp = frameSharedVarLocalVar.kt, 4) Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 6595f1ebc14..0dd3d7c1c17 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -4452,6 +4452,63 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CapturedVarsOptimization extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInCapturedVarsOptimization() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures/capturedVarsOptimization"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("capturedInCrossinline.kt") + public void testCapturedInCrossinline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInCrossinline.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyAssign.kt") + public void testCapturedInInlineOnlyAssign() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyAssign.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyCAO.kt") + public void testCapturedInInlineOnlyCAO() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyCAO.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyIncrDecr.kt") + public void testCapturedInInlineOnlyIncrDecr() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIncrDecr.kt"); + doTest(fileName); + } + + @TestMetadata("capturedInInlineOnlyIndexedCAO.kt") + public void testCapturedInInlineOnlyIndexedCAO() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedInInlineOnlyIndexedCAO.kt"); + doTest(fileName); + } + + @TestMetadata("capturedVarsOfSize2.kt") + public void testCapturedVarsOfSize2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/capturedVarsOfSize2.kt"); + doTest(fileName); + } + + @TestMetadata("sharedSlotsWithCapturedVars.kt") + public void testSharedSlotsWithCapturedVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt"); + doTest(fileName); + } + + @TestMetadata("withCoroutines.kt") + public void testWithCoroutines() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/withCoroutines.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)