KT-11499 Normalize stacks on return from an inline function when the function is inlined at call site.

This commit is contained in:
Dmitry Petrov
2016-04-07 20:00:12 +03:00
parent a6044c81ff
commit aca7050656
19 changed files with 502 additions and 87 deletions
@@ -528,7 +528,7 @@ public class FunctionCodegen {
catch (Throwable t) {
String bytecode = renderByteCodeIfAvailable(mv);
throw new CompilationException(
"wrong code generated" +
"wrong code generated\n" +
(description != null ? " for " + description : "") +
t.getClass().getName() +
" " +
@@ -314,11 +314,21 @@ public class InlineCodegenUtil {
//marked return could be either non-local or local in case of labeled lambda self-returns
public static boolean isMarkedReturn(@NotNull AbstractInsnNode returnIns) {
if (!isReturnOpcode(returnIns.getOpcode())) {
return false;
return getMarkedReturnLabelOrNull(returnIns) != null;
}
public static @Nullable String getMarkedReturnLabelOrNull(@NotNull AbstractInsnNode returnInsn) {
if (!isReturnOpcode(returnInsn.getOpcode())) {
return null;
}
AbstractInsnNode globalFlag = returnIns.getPrevious();
return globalFlag instanceof MethodInsnNode && NON_LOCAL_RETURN.equals(((MethodInsnNode)globalFlag).owner);
AbstractInsnNode previous = returnInsn.getPrevious();
if (previous instanceof MethodInsnNode) {
MethodInsnNode marker = (MethodInsnNode) previous;
if (NON_LOCAL_RETURN.equals(marker.owner)) {
return marker.name;
}
}
return null;
}
public static void generateGlobalReturnFlag(@NotNull InstructionAdapter iv, @NotNull String labelName) {
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.codegen.StackValue;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.optimization.MandatoryMethodTransformer;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.utils.SmartList;
import org.jetbrains.kotlin.utils.SmartSet;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
@@ -35,6 +36,7 @@ import org.jetbrains.org.objectweb.asm.commons.Method;
import org.jetbrains.org.objectweb.asm.commons.RemappingMethodAdapter;
import org.jetbrains.org.objectweb.asm.tree.*;
import org.jetbrains.org.objectweb.asm.tree.analysis.*;
import org.jetbrains.org.objectweb.asm.util.Printer;
import java.util.*;
@@ -121,7 +123,7 @@ public class MethodInliner {
int finallyDeepShift
) {
//analyze body
MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node, finallyDeepShift);
MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node, labelOwner, finallyDeepShift);
//substitute returns with "goto end" instruction to keep non local returns in lambdas
Label end = new Label();
@@ -398,44 +400,12 @@ public class MethodInliner {
}
@NotNull
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, int finallyDeepShift) {
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, int finallyDeepShift) {
node = prepareNode(node, finallyDeepShift);
try {
new MandatoryMethodTransformer().transform("fake", node);
}
catch (Throwable e) {
throw wrapException(e, node, "couldn't inline method call");
}
Frame<SourceValue>[] sources = analyzeMethodNodeBeforeInline(node);
LocalReturnsNormalizer localReturnsNormalizer = LocalReturnsNormalizer.createFor(node, labelOwner, sources);
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) {
@NotNull
@Override
protected Frame<SourceValue> newFrame(
int nLocals, int nStack
) {
return new Frame<SourceValue>(nLocals, nStack) {
@Override
public void execute(
@NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter
) throws AnalyzerException {
if (insn.getOpcode() == Opcodes.RETURN) {
//there is exception on void non local return in frame
return;
}
super.execute(insn, interpreter);
}
};
}
};
Frame<SourceValue>[] sources;
try {
sources = analyzer.analyze("fake", node);
}
catch (AnalyzerException e) {
throw wrapException(e, node, "couldn't inline method call");
}
Set<AbstractInsnNode> toDelete = SmartSet.create();
InsnList instructions = node.instructions;
AbstractInsnNode cur = instructions.getFirst();
@@ -547,9 +517,44 @@ public class MethodInliner {
}
}
localReturnsNormalizer.transform(node);
return node;
}
private Frame<SourceValue>[] analyzeMethodNodeBeforeInline(@NotNull MethodNode node) {
try {
new MandatoryMethodTransformer().transform("fake", node);
}
catch (Throwable e) {
throw wrapException(e, node, "couldn't inline method call");
}
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) {
@NotNull
@Override
protected Frame<SourceValue> newFrame(int nLocals, int nStack) {
return new Frame<SourceValue>(nLocals, nStack) {
@Override
public void execute(@NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter) throws AnalyzerException {
// This can be a void non-local return from a non-void method; Frame#execute would throw and do nothing else.
if (insn.getOpcode() == Opcodes.RETURN) return;
super.execute(insn, interpreter);
}
};
}
};
Frame<SourceValue>[] sources;
try {
sources = analyzer.analyze("fake", node);
}
catch (AnalyzerException e) {
throw wrapException(e, node, "couldn't inline method call");
}
return sources;
}
private static boolean isEmptyTryInterval(@NotNull TryCatchBlockNode tryCatchBlockNode) {
LabelNode start = tryCatchBlockNode.start;
AbstractInsnNode end = tryCatchBlockNode.end;
@@ -758,20 +763,14 @@ public class MethodInliner {
AbstractInsnNode insnNode = instructions.getFirst();
while (insnNode != null) {
if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) {
AbstractInsnNode previous = insnNode.getPrevious();
MethodInsnNode flagNode;
boolean isLocalReturn = true;
String labelName = null;
if (previous != null && previous instanceof MethodInsnNode && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) {
flagNode = (MethodInsnNode) previous;
labelName = flagNode.name;
}
String labelName = InlineCodegenUtil.getMarkedReturnLabelOrNull(insnNode);
if (labelName != null) {
isLocalReturn = labelOwner.isMyLabel(labelName);
//remove global return flag
if (isLocalReturn) {
instructions.remove(previous);
instructions.remove(insnNode.getPrevious());
}
}
@@ -783,7 +782,7 @@ public class MethodInliner {
insnNode = jumpInsnNode;
}
//genetate finally block before nonLocalReturn flag/return/goto
//generate finally block before nonLocalReturn flag/return/goto
LabelNode label = new LabelNode();
instructions.insert(insnNode, label);
result.add(new PointForExternalFinallyBlocks(getInstructionToInsertFinallyBefore(insnNode, isLocalReturn),
@@ -795,6 +794,119 @@ public class MethodInliner {
return result;
}
private static class LocalReturnsNormalizer {
private static class LocalReturn {
private final AbstractInsnNode returnInsn;
private final AbstractInsnNode insertBeforeInsn;
private final Frame<SourceValue> frame;
public LocalReturn(
@NotNull AbstractInsnNode returnInsn,
@NotNull AbstractInsnNode insertBeforeInsn,
@NotNull Frame<SourceValue> frame
) {
this.returnInsn = returnInsn;
this.insertBeforeInsn = insertBeforeInsn;
this.frame = frame;
}
public void transform(@NotNull InsnList insnList, int returnVariableIndex) {
boolean isReturnWithValue = returnInsn.getOpcode() != Opcodes.RETURN;
int expectedStackSize = isReturnWithValue ? 1 : 0;
int actualStackSize = frame.getStackSize();
if (expectedStackSize == actualStackSize) return;
int stackSize = actualStackSize;
if (isReturnWithValue) {
int storeOpcode = Opcodes.ISTORE + returnInsn.getOpcode() - Opcodes.IRETURN;
insnList.insertBefore(insertBeforeInsn, new VarInsnNode(storeOpcode, returnVariableIndex));
stackSize--;
}
while (stackSize > 0) {
int stackElementSize = frame.getStack(stackSize - 1).getSize();
int popOpcode = stackElementSize == 1 ? Opcodes.POP : Opcodes.POP2;
insnList.insertBefore(insertBeforeInsn, new InsnNode(popOpcode));
stackSize--;
}
if (isReturnWithValue) {
int loadOpcode = Opcodes.ILOAD + returnInsn.getOpcode() - Opcodes.IRETURN;
insnList.insertBefore(insertBeforeInsn, new VarInsnNode(loadOpcode, returnVariableIndex));
}
}
}
private final List<LocalReturn> localReturns = new SmartList<LocalReturn>();
private boolean needsReturnVariable = false;
private int returnOpcode = -1;
private void addLocalReturnToTransform(
@NotNull AbstractInsnNode returnInsn,
@NotNull AbstractInsnNode insertBeforeInsn,
@NotNull Frame<SourceValue> sourceValueFrame
) {
assert InlineCodegenUtil.isReturnOpcode(returnInsn.getOpcode()) : "return instruction expected";
assert returnOpcode < 0 || returnOpcode == returnInsn.getOpcode() :
"Return op should be " + Printer.OPCODES[returnOpcode] + ", got " + Printer.OPCODES[returnInsn.getOpcode()];
returnOpcode = returnInsn.getOpcode();
localReturns.add(new LocalReturn(returnInsn, insertBeforeInsn, sourceValueFrame));
if (returnInsn.getOpcode() != Opcodes.RETURN && sourceValueFrame.getStackSize() > 1) {
needsReturnVariable = true;
}
}
public void transform(MethodNode methodNode) {
int returnVariableIndex = -1;
if (needsReturnVariable) {
returnVariableIndex = methodNode.maxLocals;
methodNode.maxLocals++;
}
for (LocalReturn localReturn : localReturns) {
localReturn.transform(methodNode.instructions, returnVariableIndex);
}
}
public static LocalReturnsNormalizer createFor(
@NotNull MethodNode methodNode,
@NotNull LabelOwner owner,
@NotNull Frame<SourceValue>[] frames
) {
LocalReturnsNormalizer result = new LocalReturnsNormalizer();
AbstractInsnNode[] instructions = methodNode.instructions.toArray();
for (int i = 0; i < instructions.length; ++i) {
Frame<SourceValue> frame = frames[i];
// Don't care about dead code, it will be eliminated
if (frame == null) continue;
AbstractInsnNode insnNode = instructions[i];
if (!InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) continue;
AbstractInsnNode insertBeforeInsn = insnNode;
// TODO extract isLocalReturn / isNonLocalReturn, see processReturns
String labelName = getMarkedReturnLabelOrNull(insnNode);
if (labelName != null) {
if (!owner.isMyLabel(labelName)) continue;
insertBeforeInsn = insnNode.getPrevious();
}
result.addLocalReturnToTransform(insnNode, insertBeforeInsn, frame);
}
return result;
}
}
@NotNull
private static AbstractInsnNode getInstructionToInsertFinallyBefore(@NotNull AbstractInsnNode nonLocalReturnOrJump, boolean isLocal) {
return isLocal ? nonLocalReturnOrJump : nonLocalReturnOrJump.getPrevious();
@@ -16,9 +16,7 @@
package org.jetbrains.kotlin.codegen.optimization.boxing
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
import org.jetbrains.kotlin.codegen.optimization.replaceNodeGetNext
@@ -39,55 +37,55 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
private class Transformer(val methodNode: MethodNode) {
private val insnList = methodNode.instructions
private val frames: Array<Frame<SourceValue>?> = Analyzer<SourceValue>(SourceInterpreter()).analyze("fake", methodNode)
private val insns = insnList.toArray()
private val dontTouchInsns = hashSetOf<AbstractInsnNode>()
private val transformations = hashMapOf<AbstractInsnNode, () -> Unit>()
private val removableNops = hashSetOf<InsnNode>()
private val frames: Array<out Frame<SourceValue>?> = analyzeMethodBody()
fun transform() {
computeDontTouchInsns()
computeTransformations()
transformations.values.forEach { it() }
postprocessNops()
}
private fun computeDontTouchInsns() {
for (i in 0..insns.lastIndex) {
val frame = frames[i] ?: continue
val insn = insns[i]
private fun analyzeMethodBody(): Array<out Frame<SourceValue>?> =
Analyzer<SourceValue>(object : SourceInterpreter() {
override fun naryOperation(insn: AbstractInsnNode, values: MutableList<out SourceValue>): SourceValue {
for (value in values) {
dontTouchInsns.addAll(value.insns)
}
return super.naryOperation(insn, values)
}
when (insn.opcode) {
Opcodes.DUP ->
dontTouchWordsOnTop(i, frame, 1)
Opcodes.DUP_X1 ->
dontTouchWordsOnTop(i, frame, 2)
Opcodes.DUP_X2 ->
dontTouchWordsOnTop(i, frame, 3)
Opcodes.DUP2 ->
dontTouchWordsOnTop(i, frame, 2)
Opcodes.DUP2_X1 ->
dontTouchWordsOnTop(i, frame, 3)
Opcodes.DUP2_X2 ->
dontTouchWordsOnTop(i, frame, 4)
Opcodes.SWAP ->
dontTouchWordsOnTop(i, frame, 2)
}
}
}
override fun copyOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
dontTouchInsns.addAll(value.insns)
return super.copyOperation(insn, value)
}
override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
if (insn.opcode != Opcodes.CHECKCAST) {
dontTouchInsns.addAll(value.insns)
}
return super.unaryOperation(insn, value)
}
override fun binaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue): SourceValue {
dontTouchInsns.addAll(value1.insns)
dontTouchInsns.addAll(value2.insns)
return super.binaryOperation(insn, value1, value2)
}
override fun ternaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue, value3: SourceValue): SourceValue {
dontTouchInsns.addAll(value1.insns)
dontTouchInsns.addAll(value2.insns)
dontTouchInsns.addAll(value3.insns)
return super.ternaryOperation(insn, value1, value2, value3)
}
}).analyze("fake", methodNode)
private fun dontTouchWordsOnTop(at: Int, frame: Frame<SourceValue>, expectedWords: Int) {
var words = 0
var offset = 0
while (words < expectedWords) {
val value = frame.peek(offset) ?: throwIllegalStackInsn(at)
offset++
words += value.size
dontTouchInsns.addAll(value.insns)
}
if (words != expectedWords) throwIllegalStackInsn(at)
}
private fun computeTransformations() {
transformations.clear()
+1
View File
@@ -0,0 +1 @@
fun box() = null ?: null ?: "OK"
@@ -0,0 +1,10 @@
// FILE: 1.kt
fun foo(x: Any?, y: Any?) = null
inline fun test(value: Any?): String? {
return foo(null, value ?: return null)
}
// FILE: 2.kt
fun box(): String =
test(null) ?: "OK"
@@ -0,0 +1,10 @@
// FILE: 1.kt
fun foo(x: Any?, y: Any?) = null
inline fun test(value: Any?): String? {
return foo(null, if (value != null) value else return null)
}
// FILE: 2.kt
fun box(): String =
test(null) ?: "OK"
@@ -0,0 +1,12 @@
// FILE: 1.kt
object CrashMe {
fun <T> crash(value: T): T? = null
}
internal inline fun <reified T> crashMe(value: T?): T? {
return CrashMe.crash(value ?: return null)
}
// FILE: 2.kt
fun box(): String =
crashMe<String>(null) ?: "OK"
@@ -0,0 +1,10 @@
// FILE: 1.kt
fun foo1(x: Long, xx: Int, y: Any?) = null
inline fun test1(value: Any?): String? {
return foo1(0L, 0, value ?: return null)
}
// FILE: 2.kt
fun box(): String =
test1(null) ?: "OK"
@@ -0,0 +1,10 @@
// FILE: 1.kt
fun foo2(x: Int, xx: Long, y: Any?) = null
inline fun test2(value: Any?): String? {
return foo2(0, 0L, value ?: return null)
}
// FILE: 2.kt
fun box(): String =
test2(null) ?: "OK"
@@ -0,0 +1,10 @@
// FILE: 1.kt
fun foo3(x: Int, xx: Long, xxx: Int, y: Any?) = null
inline fun test3(value: Any?): String? {
return foo3(0, 0L, 0, value ?: return null)
}
// FILE: 2.kt
fun box(): String =
test3(null) ?: "OK"
@@ -0,0 +1,15 @@
// FILE: 1.kt
inline fun run(f: () -> Unit) = f()
inline fun withAny(f: Any.() -> Unit) = Any().f()
// FILE: 2.kt
fun foo(x: Any?, y: Any?) {}
fun box(): String {
run outer@{
withAny inner@{
foo(null, null ?: return@outer)
}
}
return "OK"
}
@@ -0,0 +1,15 @@
// FILE: 1.kt
inline fun run(f: () -> Unit) = f()
inline fun withAny(f: Any.() -> Unit) = Any().f()
// FILE: 2.kt
fun foo(x: Any?, y: Any?) {}
fun box(): String {
run outer@{
withAny inner@{
foo(null, null ?: return@inner)
}
}
return "OK"
}
@@ -0,0 +1,17 @@
// FILE: 1.kt
inline fun run(f: () -> Unit) = f()
inline fun withAny(f: Any.() -> Unit) = Any().f()
inline fun foo(x: Any?, y: Any?, f: () -> Unit) = f()
// FILE: 2.kt
fun box(): String {
run outer@{
withAny inner@{
foo(null, 0 ?: return@outer) {
foo(null, null ?: return@inner) {}
}
}
}
return "OK"
}
@@ -0,0 +1,12 @@
// FILE: 1.kt
fun foo(x: Any?, y: Any?) = 0L
inline fun test(value: Any?): Long {
return foo(null, value ?: return 1L)
}
// FILE: 2.kt
fun box(): String {
val t = test(null)
return if (t == 1L) "OK" else "fail: t=$t"
}
@@ -0,0 +1,17 @@
// FILE: 1.kt
fun foo(x: Any?, y: Any?) = null
var finallyFlag = false
inline fun test(value: Any?): String? {
try {
return foo(null, value ?: return null)
}
finally {
finallyFlag = true
}
}
// FILE: 2.kt
fun box(): String =
test(null) ?: if (finallyFlag) "OK" else "finallyFlag not set"
@@ -5326,6 +5326,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("nullNullOk.kt")
public void testNullNullOk() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/elvis/nullNullOk.kt");
doTest(fileName);
}
@TestMetadata("primitive.kt")
public void testPrimitive() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/elvis/primitive.kt");
@@ -1997,6 +1997,81 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
}
}
@TestMetadata("compiler/testData/codegen/boxInline/stackOnReturn")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class StackOnReturn extends AbstractBlackBoxInlineCodegenTest {
public void testAllFilesPresentInStackOnReturn() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/stackOnReturn"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("elvis.kt")
public void testElvis() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/elvis.kt");
doTest(fileName);
}
@TestMetadata("ifThenElse.kt")
public void testIfThenElse() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/ifThenElse.kt");
doTest(fileName);
}
@TestMetadata("kt11499.kt")
public void testKt11499() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/kt11499.kt");
doTest(fileName);
}
@TestMetadata("mixedTypesOnStack1.kt")
public void testMixedTypesOnStack1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/mixedTypesOnStack1.kt");
doTest(fileName);
}
@TestMetadata("mixedTypesOnStack2.kt")
public void testMixedTypesOnStack2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/mixedTypesOnStack2.kt");
doTest(fileName);
}
@TestMetadata("mixedTypesOnStack3.kt")
public void testMixedTypesOnStack3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/mixedTypesOnStack3.kt");
doTest(fileName);
}
@TestMetadata("nonLocalReturn1.kt")
public void testNonLocalReturn1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/nonLocalReturn1.kt");
doTest(fileName);
}
@TestMetadata("nonLocalReturn2.kt")
public void testNonLocalReturn2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/nonLocalReturn2.kt");
doTest(fileName);
}
@TestMetadata("nonLocalReturn3.kt")
public void testNonLocalReturn3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/nonLocalReturn3.kt");
doTest(fileName);
}
@TestMetadata("returnLong.kt")
public void testReturnLong() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/returnLong.kt");
doTest(fileName);
}
@TestMetadata("tryFinally.kt")
public void testTryFinally() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/tryFinally.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/syntheticAccessors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1997,6 +1997,81 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
}
}
@TestMetadata("compiler/testData/codegen/boxInline/stackOnReturn")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class StackOnReturn extends AbstractCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInStackOnReturn() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/stackOnReturn"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("elvis.kt")
public void testElvis() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/elvis.kt");
doTest(fileName);
}
@TestMetadata("ifThenElse.kt")
public void testIfThenElse() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/ifThenElse.kt");
doTest(fileName);
}
@TestMetadata("kt11499.kt")
public void testKt11499() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/kt11499.kt");
doTest(fileName);
}
@TestMetadata("mixedTypesOnStack1.kt")
public void testMixedTypesOnStack1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/mixedTypesOnStack1.kt");
doTest(fileName);
}
@TestMetadata("mixedTypesOnStack2.kt")
public void testMixedTypesOnStack2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/mixedTypesOnStack2.kt");
doTest(fileName);
}
@TestMetadata("mixedTypesOnStack3.kt")
public void testMixedTypesOnStack3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/mixedTypesOnStack3.kt");
doTest(fileName);
}
@TestMetadata("nonLocalReturn1.kt")
public void testNonLocalReturn1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/nonLocalReturn1.kt");
doTest(fileName);
}
@TestMetadata("nonLocalReturn2.kt")
public void testNonLocalReturn2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/nonLocalReturn2.kt");
doTest(fileName);
}
@TestMetadata("nonLocalReturn3.kt")
public void testNonLocalReturn3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/nonLocalReturn3.kt");
doTest(fileName);
}
@TestMetadata("returnLong.kt")
public void testReturnLong() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/returnLong.kt");
doTest(fileName);
}
@TestMetadata("tryFinally.kt")
public void testTryFinally() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/stackOnReturn/tryFinally.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/syntheticAccessors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)