diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/processUninitializedStores.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/processUninitializedStores.kt index 5e5983dc629..5e3c9641ab7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/processUninitializedStores.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/processUninitializedStores.kt @@ -16,10 +16,8 @@ package org.jetbrains.kotlin.codegen.coroutines -import org.jetbrains.kotlin.codegen.optimization.common.CustomFramesMethodAnalyzer -import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter -import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue -import org.jetbrains.kotlin.codegen.optimization.common.insnListOf +import org.jetbrains.kotlin.codegen.optimization.common.* +import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.* @@ -71,124 +69,161 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter * - restore constructor arguments */ internal fun processUninitializedStores(methodNode: MethodNode) { - val interpreter = UninitializedNewValueMarkerInterpreter() - val frames = CustomFramesMethodAnalyzer("fake", methodNode, interpreter, ::UninitializedNewValueFrame).analyze() - - for ((index, insn) in methodNode.instructions.toArray().withIndex()) { - val frame = frames[index] ?: continue - val uninitializedValue = frame.getUninitializedValueForConstructorCall(insn) ?: continue - - val copyUsages: Set = interpreter.uninitializedValuesToCopyUsages[uninitializedValue.newInsn]!! - assert(copyUsages.size > 0) { "At least DUP copy operation expected" } - - // Value generated by NEW wasn't store to local/field (only DUPed) - if (copyUsages.size == 1) continue - - (copyUsages + uninitializedValue.newInsn).forEach { - methodNode.instructions.remove(it) - } - - val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size - val storedTypes = arrayListOf() - var nextVarIndex = methodNode.maxLocals - - for (i in 0 until indexOfConstructorArgumentFromTopOfStack) { - val value = frame.getStack(frame.stackSize - 1 - i) - val type = value.type - methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), nextVarIndex)) - nextVarIndex += type.size - storedTypes.add(type) - } - methodNode.maxLocals = Math.max(methodNode.maxLocals, nextVarIndex) - - methodNode.instructions.insertBefore(insn, insnListOf( - TypeInsnNode(Opcodes.NEW, uninitializedValue.newInsn.desc), - InsnNode(Opcodes.DUP) - )) - - for (type in storedTypes.reversed()) { - nextVarIndex -= type.size - methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ILOAD), nextVarIndex)) - } - } + UninitializedStoresProcessor(methodNode, forCoroutines = true).run() } -private class UninitializedNewValue( - val newInsn: TypeInsnNode, val internalName: String -) : StrictBasicValue(Type.getObjectType(internalName)) { - override fun toString() = "UninitializedNewValue(internalName='$internalName')" -} +class UninitializedStoresProcessor(private val methodNode: MethodNode, private val forCoroutines: Boolean) { + private val isInSpecialMethod = methodNode.name == "" || methodNode.name == "" -private class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame(nLocals, nStack) { - override fun execute(insn: AbstractInsnNode, interpreter: Interpreter?) { - val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null + fun run() { + val interpreter = UninitializedNewValueMarkerInterpreter() - super.execute(insn, interpreter) + val frames = CustomFramesMethodAnalyzer( + "fake", methodNode, interpreter, + this::UninitializedNewValueFrame + ).analyze() - if (replaceTopValueWithInitialized) { - // Drop top value - val value = pop() as UninitializedNewValue + for ((index, insn) in methodNode.instructions.toArray().withIndex()) { + val frame = frames[index] ?: continue + val uninitializedValue = frame.getUninitializedValueForConstructorCall(insn) ?: continue - // uninitialized value become initialized after call - push(StrictBasicValue(value.type)) + val newInsn = uninitializedValue.newInsn + val copyUsages: Set = interpreter.uninitializedValuesToCopyUsages[newInsn]!! + assert(copyUsages.isNotEmpty()) { "At least DUP copy operation expected" } + + // Value generated by NEW wasn't store to local/field (only DUPed) + if (copyUsages.size == 1) continue + + methodNode.instructions.run { + removeAll(copyUsages) + if (forCoroutines) { + remove(newInsn) + } + else { + // Replace 'NEW C' instruction with "manual" initialization of class 'C': + // LDC [typeName for C] + // INVOKESTATIC java/lang/Class.forName (Ljava/lang/String;)Ljava/lang/Class; + // POP + val typeNameForClass = newInsn.desc.replace('/', '.') + insertBefore(newInsn, LdcInsnNode(typeNameForClass)) + insertBefore( + newInsn, + MethodInsnNode( + Opcodes.INVOKESTATIC, + "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", + false + ) + ) + set(newInsn, InsnNode(Opcodes.POP)) + } + } + + val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + val storedTypes = arrayListOf() + var nextVarIndex = methodNode.maxLocals + + for (i in 0 until indexOfConstructorArgumentFromTopOfStack) { + val value = frame.getStack(frame.stackSize - 1 - i) + val type = value.type + methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), nextVarIndex)) + nextVarIndex += type.size + storedTypes.add(type) + } + methodNode.maxLocals = Math.max(methodNode.maxLocals, nextVarIndex) + + methodNode.instructions.insertBefore(insn, insnListOf( + TypeInsnNode(Opcodes.NEW, newInsn.desc), + InsnNode(Opcodes.DUP) + )) + + for (type in storedTypes.reversed()) { + nextVarIndex -= type.size + methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ILOAD), nextVarIndex)) + } } } -} -/** - * @return value generated by NEW that used as 0-th argument of constructor call or null if current instruction is not constructor call - */ -private fun Frame.getUninitializedValueForConstructorCall( - insn: AbstractInsnNode -): UninitializedNewValue? { - if (!insn.isConstructorCall()) return null + private inner class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame(nLocals, nStack) { + override fun execute(insn: AbstractInsnNode, interpreter: Interpreter?) { + val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null - assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for , but ${insn.opcode} found" } - val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1 - val newValue = getStack(stackSize - (paramsCountIncludingReceiver + 1)) as? UninitializedNewValue ?: error("Expected value generated with NEW") + super.execute(insn, interpreter) - assert(getStack(stackSize - paramsCountIncludingReceiver) is UninitializedNewValue) { - "Next value after NEW should be one generated by DUP" + if (replaceTopValueWithInitialized) { + // Drop top value + val value = pop() as UninitializedNewValue + + // uninitialized value become initialized after call + push(StrictBasicValue(value.type)) + } + } } - return newValue -} + /** + * @return value generated by NEW that used as 0-th argument of constructor call or null if current instruction is not constructor call + */ + private fun Frame.getUninitializedValueForConstructorCall(insn: AbstractInsnNode): UninitializedNewValue? { + if (!insn.isConstructorCall()) return null -private fun AbstractInsnNode.isConstructorCall() = this is MethodInsnNode && this.name == "" + assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for , but ${insn.opcode} found" } + val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1 + val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue ?: + if (isInSpecialMethod) + return null + else + error("Expected value generated with NEW") -private class UninitializedNewValueMarkerInterpreter : OptimizationBasicInterpreter() { - val uninitializedValuesToCopyUsages = hashMapOf>() - override fun newOperation(insn: AbstractInsnNode): BasicValue? { - if (insn.opcode == Opcodes.NEW) { - uninitializedValuesToCopyUsages.getOrPut(insn) { mutableSetOf() } - return UninitializedNewValue(insn as TypeInsnNode, insn.desc) + assert(peek(paramsCountIncludingReceiver - 1) is UninitializedNewValue) { + "Next value after NEW should be one generated by DUP" } - return super.newOperation(insn) + + return newValue } - override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? { - if (value is UninitializedNewValue) { - uninitializedValuesToCopyUsages[value.newInsn]!!.add(insn) - return value - } - return super.copyOperation(insn, value) + private class UninitializedNewValue( + val newInsn: TypeInsnNode, + val internalName: String + ) : StrictBasicValue(Type.getObjectType(internalName)) { + override fun toString() = "UninitializedNewValue(internalName='$internalName')" } - override fun merge(v: BasicValue, w: BasicValue): BasicValue { - if (v === w) return v - if (v === StrictBasicValue.UNINITIALIZED_VALUE || w === StrictBasicValue.UNINITIALIZED_VALUE) { - return StrictBasicValue.UNINITIALIZED_VALUE + + private fun AbstractInsnNode.isConstructorCall() = this is MethodInsnNode && this.name == "" + + private class UninitializedNewValueMarkerInterpreter : OptimizationBasicInterpreter() { + val uninitializedValuesToCopyUsages = hashMapOf>() + override fun newOperation(insn: AbstractInsnNode): BasicValue? { + if (insn.opcode == Opcodes.NEW) { + uninitializedValuesToCopyUsages.getOrPut(insn) { mutableSetOf() } + return UninitializedNewValue(insn as TypeInsnNode, insn.desc) + } + return super.newOperation(insn) } - if (v is UninitializedNewValue || w is UninitializedNewValue) { - if ((v as? UninitializedNewValue)?.newInsn !== (w as? UninitializedNewValue)?.newInsn) { - // Merge of two different ANEW result is possible, but such values should not be used further + override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? { + if (value is UninitializedNewValue) { + uninitializedValuesToCopyUsages[value.newInsn]!!.add(insn) + return value + } + return super.copyOperation(insn, value) + } + + override fun merge(v: BasicValue, w: BasicValue): BasicValue { + if (v === w) return v + if (v === StrictBasicValue.UNINITIALIZED_VALUE || w === StrictBasicValue.UNINITIALIZED_VALUE) { return StrictBasicValue.UNINITIALIZED_VALUE } - return v - } + if (v is UninitializedNewValue || w is UninitializedNewValue) { + if ((v as? UninitializedNewValue)?.newInsn !== (w as? UninitializedNewValue)?.newInsn) { + // Merge of two different ANEW result is possible, but such values should not be used further + return StrictBasicValue.UNINITIALIZED_VALUE + } - return super.merge(v, w) + return v + } + + return super.merge(v, w) + } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/MandatoryMethodTransforker.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/FixStackWithLabelNormalizationMethodTransformer.kt similarity index 100% rename from compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/MandatoryMethodTransforker.kt rename to compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/FixStackWithLabelNormalizationMethodTransformer.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java index 3a8223e869e..812944c12a7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java @@ -17,9 +17,7 @@ package org.jetbrains.kotlin.codegen.optimization; import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.codegen.ClassBuilder; import org.jetbrains.kotlin.codegen.ClassBuilderFactory; -import org.jetbrains.kotlin.codegen.ClassBuilderMode; import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt index 33c62a48c77..04663015d31 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt @@ -17,9 +17,9 @@ package org.jetbrains.kotlin.codegen.optimization import org.jetbrains.kotlin.codegen.TransformationMethodVisitor -import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer -import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer +import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer +import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer @@ -35,11 +35,10 @@ class OptimizationMethodVisitor( signature: String?, exceptions: Array? ) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) { - override fun performTransformations(methodNode: MethodNode) { - MANDATORY_METHOD_TRANSFORMER.transform("fake", methodNode) + normalizationMethodTransformer.transform("fake", methodNode) if (canBeOptimized(methodNode) && !disableOptimization) { - OPTIMIZATION_TRANSFORMER.transform("fake", methodNode) + optimizationTransformer.transform("fake", methodNode) } methodNode.prepareForEmitting() } @@ -47,12 +46,13 @@ class OptimizationMethodVisitor( companion object { private val MEMORY_LIMIT_BY_METHOD_MB = 50 - private val MANDATORY_METHOD_TRANSFORMER = CompositeMethodTransformer( + val normalizationMethodTransformer = CompositeMethodTransformer( FixStackWithLabelNormalizationMethodTransformer(), + UninitializedStoresMethodTransformer(), MethodVerifier("AFTER mandatory stack transformations") ) - private val OPTIMIZATION_TRANSFORMER = CompositeMethodTransformer( + val optimizationTransformer = CompositeMethodTransformer( CapturedVarsOptimizationMethodTransformer(), RedundantNullCheckMethodTransformer(), RedundantCheckCastEliminationMethodTransformer(), diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/UninitializedStoresMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/UninitializedStoresMethodTransformer.kt new file mode 100644 index 00000000000..aab689820fa --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/UninitializedStoresMethodTransformer.kt @@ -0,0 +1,27 @@ +/* + * 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 + +import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor +import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer +import org.jetbrains.org.objectweb.asm.tree.MethodNode + +class UninitializedStoresMethodTransformer : MethodTransformer() { + override fun transform(internalClassName: String, methodNode: MethodNode) { + UninitializedStoresProcessor(methodNode, forCoroutines = false).run() + } +} \ No newline at end of file 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 77fb229f4d8..adb8fe84bcb 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 @@ -214,3 +214,6 @@ internal inline fun AbstractInsnNode.isInsn(opcod internal inline fun AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? = takeIf { it.opcode == opcode }?.safeAs()?.takeIf { it.condition() } +fun InsnList.removeAll(nodes: Collection) { + for (node in nodes) remove(node) +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/transformer/CompositeMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/transformer/CompositeMethodTransformer.kt index 5726321e506..e2e40b47b59 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/transformer/CompositeMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/transformer/CompositeMethodTransformer.kt @@ -18,8 +18,15 @@ package org.jetbrains.kotlin.codegen.optimization.transformer import org.jetbrains.org.objectweb.asm.tree.MethodNode -open class CompositeMethodTransformer(private vararg val transformers: MethodTransformer) : MethodTransformer() { +open class CompositeMethodTransformer(private val transformers: List) : MethodTransformer() { + constructor(vararg transformers: MethodTransformer?) : this(transformers.filterNotNull()) + override fun transform(internalClassName: String, methodNode: MethodNode) { transformers.forEach { it.transform(internalClassName, methodNode) } } + + companion object { + inline fun build(builder: MutableList.() -> Unit) = + CompositeMethodTransformer(ArrayList().apply { builder() }) + } } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 6a9269e3275..9da8888b465 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -179,7 +179,6 @@ class GenerationState @JvmOverloads constructor( val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA) - val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals) init { diff --git a/compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt b/compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt new file mode 100644 index 00000000000..6a07675236f --- /dev/null +++ b/compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt @@ -0,0 +1,40 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: test.kt +fun box(): String { + Foo( + logged("i", 1.let { it }), + logged("j", + Foo( + logged("k", 2.let { it }), + null + ) + ) + ) + + val result = log.toString() + if (result != "ikj") return "Fail: '$result'" + + return "OK" +} + +// FILE: util.kt +val log = StringBuilder() + +fun logged(msg: String, value: T): T { + log.append(msg) + return value +} + +// FILE: Foo.kt +class Foo(i: Int, j: Foo?) { + init { + log.append("") + } + + companion object { + init { + log.append("") + } + } +} diff --git a/compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt b/compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt new file mode 100644 index 00000000000..9292242e2b6 --- /dev/null +++ b/compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt @@ -0,0 +1,43 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: test.kt +fun box(): String { + Outer().Inner( + logged("i;", 1.let { it }), + logged("j;", 2.let { it }) + ) + + val result = log.toString() + if (result != "Foo.;i;j;Foo.;Inner.;") return "Fail: '$result'" + + return "OK" +} + +// FILE: util.kt +val log = StringBuilder() + +fun logged(msg: String, value: T): T { + log.append(msg) + return value +} + +// FILE: Foo.kt +open class Foo { + init { + log.append("Foo.;") + } + + companion object { + init { + log.append("Foo.;") + } + } +} + +class Outer { + inner class Inner(val x: Int, val y: Int) : Foo() { + init { + log.append("Inner.;") + } + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt b/compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt new file mode 100644 index 00000000000..7061885f7aa --- /dev/null +++ b/compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt @@ -0,0 +1,41 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: test.kt +fun box(): String { + class Local(val i: Int, val j: Int) : Foo() { + init { + log.append("Local.;") + } + } + + Local( + logged("i;", 1.let { it }), + logged("j;", 2.let { it }) + ) + + val result = log.toString() + if (result != "Foo.;i;j;Foo.;Local.;") return "Fail: '$result'" + + return "OK" +} + +// FILE: util.kt +val log = StringBuilder() + +fun logged(msg: String, value: T): T { + log.append(msg) + return value +} + +// FILE: Foo.kt +open class Foo { + init { + log.append("Foo.;") + } + + companion object { + init { + log.append("Foo.;") + } + } +} diff --git a/compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt b/compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt new file mode 100644 index 00000000000..dcba51be98f --- /dev/null +++ b/compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt @@ -0,0 +1,32 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: test.kt +fun box(): String { + Foo(logged("i", 1), logged("j", 2)) + + val result = log.toString() + if (result != "ij") return "Fail: '$result'" + + return "OK" +} + +// FILE: util.kt +val log = StringBuilder() + +fun logged(msg: String, value: T): T { + log.append(msg) + return value +} + +// FILE: Foo.kt +class Foo(i: Int, j: Int) { + init { + log.append("") + } + + companion object { + init { + log.append("") + } + } +} diff --git a/compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt b/compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt new file mode 100644 index 00000000000..4e3e586d5c4 --- /dev/null +++ b/compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt @@ -0,0 +1,35 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: test.kt +fun box(): String { + Foo( + logged("i", try { 1 } catch (e: Exception) { 42 }), + logged("j", 2) + ) + + val result = log.toString() + if (result != "ij") return "Fail: '$result'" + + return "OK" +} + +// FILE: util.kt +val log = StringBuilder() + +fun logged(msg: String, value: T): T { + log.append(msg) + return value +} + +// FILE: Foo.kt +class Foo(i: Int, j: Int) { + init { + log.append("") + } + + companion object { + init { + log.append("") + } + } +} 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 91a730010b4..da495a1c126 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 @@ -4275,6 +4275,45 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/constructorCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConstructorCall extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInConstructorCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("inlineFunInConstructorCallEvaluationOrder.kt") + public void testInlineFunInConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("inlineFunInInnerClassConstructorCall.kt") + public void testInlineFunInInnerClassConstructorCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt"); + doTest(fileName); + } + + @TestMetadata("inlineFunInLocalClassConstructorCall.kt") + public void testInlineFunInLocalClassConstructorCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt"); + doTest(fileName); + } + + @TestMetadata("regularConstructorCallEvaluationOrder.kt") + public void testRegularConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("tryCatchInConstructorCallEvaluationOrder.kt") + public void testTryCatchInConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/controlStructures") @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 d893bfb2768..315fdb42ca4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4275,6 +4275,45 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/constructorCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConstructorCall extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInConstructorCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("inlineFunInConstructorCallEvaluationOrder.kt") + public void testInlineFunInConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("inlineFunInInnerClassConstructorCall.kt") + public void testInlineFunInInnerClassConstructorCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt"); + doTest(fileName); + } + + @TestMetadata("inlineFunInLocalClassConstructorCall.kt") + public void testInlineFunInLocalClassConstructorCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt"); + doTest(fileName); + } + + @TestMetadata("regularConstructorCallEvaluationOrder.kt") + public void testRegularConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("tryCatchInConstructorCallEvaluationOrder.kt") + public void testTryCatchInConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/controlStructures") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 5fff2fbb6f4..f4f8af9a1e3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -4275,6 +4275,45 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/constructorCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConstructorCall extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInConstructorCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("inlineFunInConstructorCallEvaluationOrder.kt") + public void testInlineFunInConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("inlineFunInInnerClassConstructorCall.kt") + public void testInlineFunInInnerClassConstructorCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInInnerClassConstructorCall.kt"); + doTest(fileName); + } + + @TestMetadata("inlineFunInLocalClassConstructorCall.kt") + public void testInlineFunInLocalClassConstructorCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/inlineFunInLocalClassConstructorCall.kt"); + doTest(fileName); + } + + @TestMetadata("regularConstructorCallEvaluationOrder.kt") + public void testRegularConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/regularConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + + @TestMetadata("tryCatchInConstructorCallEvaluationOrder.kt") + public void testTryCatchInConstructorCallEvaluationOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/constructorCall/tryCatchInConstructorCallEvaluationOrder.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/controlStructures") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 ec688353b26..bd63ea2bcd9 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 @@ -4905,6 +4905,15 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/constructorCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConstructorCall extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInConstructorCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/constructorCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + } + @TestMetadata("compiler/testData/codegen/box/controlStructures") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)