From 8092717da4987d0161c01776442e19764e82c53a Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Thu, 5 Jun 2014 16:56:49 +0400 Subject: [PATCH] Support non local return generation, support non local return inlining --- .../jet/codegen/ExpressionCodegen.java | 90 ++++++++++++++-- .../inline/AnonymousObjectTransformer.java | 6 +- .../jet/codegen/inline/InlineCodegen.java | 85 ++++++++++++--- .../jet/codegen/inline/InlineCodegenUtil.java | 43 +++++++- .../inline/InliningInstructionAdapter.java | 50 +++++++++ .../jet/codegen/inline/LabelOwner.java | 40 +++++++ .../jet/codegen/inline/LambdaInfo.java | 30 +++--- .../jet/codegen/inline/MethodInliner.java | 98 +++++++++++++---- .../jet/codegen/inline/RemapVisitor.java | 50 +-------- .../jet/lang/resolve/BindingContextUtils.java | 18 ++++ .../ControlStructureTypingVisitor.java | 10 +- .../deparenthesize/bracket.1.kt | 30 ++++++ .../deparenthesize/bracket.2.kt | 5 + .../deparenthesize/labeled.1.kt | 30 ++++++ .../deparenthesize/labeled.2.kt | 5 + .../nonLocalReturns/explicitLocalReturn.1.kt | 30 ++++++ .../nonLocalReturns/explicitLocalReturn.2.kt | 5 + .../nonLocalReturns/justReturnInLambda.1.kt | 17 +++ .../nonLocalReturns/justReturnInLambda.2.kt | 5 + .../nonLocalReturns/nestedNonLocals.1.kt | 73 +++++++++++++ .../nonLocalReturns/nestedNonLocals.2.kt | 5 + .../nonLocalReturns/noInlineLocalReturn.1.kt | 25 +++++ .../nonLocalReturns/noInlineLocalReturn.2.kt | 9 ++ .../nonLocalReturns/propertyAccessors.1.kt | 30 ++++++ .../nonLocalReturns/propertyAccessors.2.kt | 5 + .../boxInline/nonLocalReturns/simple.1.kt | 51 +++++++++ .../boxInline/nonLocalReturns/simple.2.kt | 5 + .../boxInline/nonLocalReturns/simpleVoid.1.kt | 30 ++++++ .../boxInline/nonLocalReturns/simpleVoid.2.kt | 5 + .../nonLocalReturns/tryFinally/external.1.kt | 100 ++++++++++++++++++ .../nonLocalReturns/tryFinally/external.2.kt | 5 + .../AbstractBlackBoxCodegenTest.java | 10 +- .../BlackBoxInlineCodegenTestGenerated.java | 85 ++++++++++++++- ...bstractCompileKotlinAgainstKotlinTest.java | 8 +- ...otlinAgainstInlineKotlinTestGenerated.java | 85 ++++++++++++++- 35 files changed, 1057 insertions(+), 121 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/inline/InliningInstructionAdapter.java create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/inline/LabelOwner.java create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/simple.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/simple.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.2.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index af203a1a0a0..607121ee970 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -32,6 +32,7 @@ import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.codegen.binding.MutableClosure; import org.jetbrains.jet.codegen.context.*; import org.jetbrains.jet.codegen.inline.InlineCodegen; +import org.jetbrains.jet.codegen.inline.InlineCodegenUtil; import org.jetbrains.jet.codegen.inline.NameGenerator; import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethod; import org.jetbrains.jet.codegen.state.GenerationState; @@ -41,6 +42,7 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; import org.jetbrains.jet.lang.evaluate.EvaluatePackage; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.calls.model.*; import org.jetbrains.jet.lang.resolve.calls.util.CallMaker; @@ -63,6 +65,7 @@ import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.renderer.DescriptorRenderer; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; +import org.jetbrains.org.objectweb.asm.Opcodes; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.org.objectweb.asm.commons.Method; @@ -73,13 +76,13 @@ import static org.jetbrains.jet.codegen.AsmUtil.*; import static org.jetbrains.jet.codegen.JvmCodegenUtil.*; import static org.jetbrains.jet.codegen.binding.CodegenBinding.*; import static org.jetbrains.jet.lang.resolve.BindingContext.*; -import static org.jetbrains.jet.lang.resolve.BindingContextUtils.getNotNull; -import static org.jetbrains.jet.lang.resolve.BindingContextUtils.isVarCapturedInClosure; +import static org.jetbrains.jet.lang.resolve.BindingContextUtils.*; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*; import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass; import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin; import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER; -import static org.jetbrains.org.objectweb.asm.Opcodes.*; +import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.jetbrains.org.objectweb.asm.Opcodes.GETFIELD; public class ExpressionCodegen extends JetVisitor implements LocalLookup { private static final Set INTEGRAL_RANGES = KotlinBuiltIns.getInstance().getIntegralRanges(); @@ -1530,7 +1533,7 @@ public class ExpressionCodegen extends JetVisitor implem } } - private boolean hasFinallyBLocks() { + public boolean hasFinallyBLocks() { for (BlockStackElement element : blockStackElements) { if (element instanceof FinallyBlockStackElement) { return true; @@ -1575,23 +1578,64 @@ public class ExpressionCodegen extends JetVisitor implem @Override public StackValue visitReturnExpression(@NotNull JetReturnExpression expression, StackValue receiver) { JetExpression returnedExpression = expression.getReturnedExpression(); + CallableMemberDescriptor descriptor = getContext().getContextDescriptor(); + NonLocalReturnInfo nonLocalReturn = getNonLocalReturnInfo(descriptor, expression); + boolean isNonLocalReturn = nonLocalReturn != null; + if (isNonLocalReturn && !state.isInlineEnabled()) { + throw new CompilationException("Non local returns requires enabled inlining", null, expression); + } + + Type returnType = isNonLocalReturn ? nonLocalReturn.returnType : this.returnType; if (returnedExpression != null) { gen(returnedExpression, returnType); - boolean hasFinallyBLocks = hasFinallyBLocks(); - if (hasFinallyBLocks) { + } + + generateFinallyBlocksIfNeeded(returnType); + + if (isNonLocalReturn) { + InlineCodegenUtil.generateGlobalReturnFlag(v, nonLocalReturn.labelName); + } + v.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); + + return StackValue.none(); + } + + public void generateFinallyBlocksIfNeeded(Type returnType) { + if (hasFinallyBLocks()) { + if (!Type.VOID_TYPE.equals(returnType)) { int returnValIndex = myFrameMap.enterTemp(returnType); StackValue.local(returnValIndex, returnType).store(returnType, v); doFinallyOnReturn(); StackValue.local(returnValIndex, returnType).put(returnType, v); myFrameMap.leaveTemp(returnType); } - v.areturn(returnType); + else { + doFinallyOnReturn(); + } } - else { - doFinallyOnReturn(); - v.visitInsn(RETURN); + } + + @Nullable + private NonLocalReturnInfo getNonLocalReturnInfo(@NotNull CallableMemberDescriptor descriptor, @NotNull JetReturnExpression expression) { + //call inside lambda + if (isLocalFunOrLambda(descriptor) && descriptor.getName().isSpecial()) { + if (expression.getLabelName() == null) { + //non labeled return couldn't be local in lambda + FunctionDescriptor containingFunction = + BindingContextUtils.getContainingFunctionSkipFunctionLiterals(bindingContext, descriptor, true).getFirst(); + //ROOT_LABEL to prevent clashing with existing labels + return new NonLocalReturnInfo(typeMapper.mapReturnType(containingFunction), InlineCodegenUtil.ROOT_LABEL); + } + + PsiElement element = bindingContext.get(LABEL_TARGET, expression.getTargetLabel()); + if (element != callableDescriptorToDeclaration(bindingContext, context.getContextDescriptor())) { + DeclarationDescriptor elementDescriptor = typeMapper.getBindingContext().get(DECLARATION_TO_DESCRIPTOR, element); + assert element != null : "Expression should be not null " + expression.getText(); + assert elementDescriptor != null : "Descriptor should be not null: " + element.getText(); + return new NonLocalReturnInfo(typeMapper.mapReturnType((CallableDescriptor) elementDescriptor), expression.getLabelName()); + } } - return StackValue.none(); + return null; } public void returnExpression(JetExpression expr) { @@ -3979,4 +4023,28 @@ The "returned" value of try expression with no finally is either the last expres Name name = context.getContextDescriptor().getName(); return nameGenerator.subGenerator((name.isSpecial() ? "$special" : name.asString()) + "$$inlined" ); } + + public Type getReturnType() { + return returnType; + } + + public Stack getBlockStackElements() { + return new Stack(blockStackElements); + } + + public void addBlockStackElementsForNonLocalReturns(@NotNull Stack elements) { + blockStackElements.addAll(elements); + } + + private static class NonLocalReturnInfo { + + final Type returnType; + + final String labelName; + + private NonLocalReturnInfo(Type type, String name) { + returnType = type; + labelName = name; + } + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/AnonymousObjectTransformer.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/AnonymousObjectTransformer.java index 271517485b6..137d91d09ff 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/AnonymousObjectTransformer.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/AnonymousObjectTransformer.java @@ -187,7 +187,8 @@ public class AnonymousObjectTransformer { MethodInliner inliner = new MethodInliner(sourceNode, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")), remapper, isSameModule, "Transformer for " + invocation.getOwnerInternalName()); - InlineResult result = inliner.doInline(resultVisitor, new LocalVarRemapper(parameters, 0), false); + + InlineResult result = inliner.doInline(resultVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE); resultVisitor.visitMaxs(-1, -1); return result; } @@ -272,7 +273,8 @@ public class AnonymousObjectTransformer { MethodInliner inliner = new MethodInliner(constructor, constructorParameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")), remapper, isSameModule, "Transformer for constructor of " + invocation.getOwnerInternalName()); - InlineResult result = inliner.doInline(capturedFieldInitializer, new LocalVarRemapper(constructorParameters, 0), false); + InlineResult result = inliner.doInline(capturedFieldInitializer, new LocalVarRemapper(constructorParameters, 0), false, + LabelOwner.NOT_APPLICABLE); constructorVisitor.visitMaxs(-1, -1); AsmUtil.genClosureFields(capturedFieldsToGenerate, classBuilder); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java index d9d85c89d83..4e59b273aaa 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java @@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.codegen.*; +import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.MethodContext; import org.jetbrains.jet.codegen.context.PackageContext; @@ -221,7 +222,31 @@ public class InlineCodegen implements CallGenerator { LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize); - return inliner.doInline(codegen.v, remapper); + + MethodNode adapter = InlineCodegenUtil.createEmptyMethodNode(); + InlineResult result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL); + + LabelOwner labelOwner = new LabelOwner() { + + final CallableMemberDescriptor descriptor = codegen.getContext().getContextDescriptor(); + + final boolean isLambda = CodegenBinding.isLocalFunOrLambda(descriptor) && descriptor.getName().isSpecial(); + + @Override + public boolean isMyLabel(@NotNull String name) { + if (InlineCodegenUtil.ROOT_LABEL.equals(name)) { + return !isLambda; + } + else { + return descriptor.getName().asString().equals(name); + } + } + }; + List infos = MethodInliner.processReturns(adapter, labelOwner, true, null); + generateAndInsertFinallyBlocks(adapter, infos); + + adapter.accept(new InliningInstructionAdapter(codegen.v)); + return result; } private void generateClosuresBodies() { @@ -270,7 +295,8 @@ public class InlineCodegen implements CallGenerator { CapturedParamDesc capturedParamInfoInLambda = activeLambda.getCapturedVars().get(capturedParamIndex); info = invocationParamBuilder.addCapturedParam(capturedParamInfoInLambda, capturedParamInfoInLambda.getFieldName()); info.setRemapValue(remappedIndex); - } else { + } + else { info = invocationParamBuilder.addNextParameter(type, false, remappedIndex); } @@ -313,13 +339,14 @@ public class InlineCodegen implements CallGenerator { return true; } - private void putParameterOnStack(ParameterInfo ... infos) { - int [] index = new int[infos.length]; + private void putParameterOnStack(ParameterInfo... infos) { + int[] index = new int[infos.length]; for (int i = 0; i < infos.length; i++) { ParameterInfo info = infos[i]; if (!info.isSkippedOrRemapped()) { index[i] = codegen.getFrameMap().enterTemp(info.getType()); - } else { + } + else { index[i] = -1; } } @@ -364,26 +391,32 @@ public class InlineCodegen implements CallGenerator { } public static boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora) { - //TODO deparenthisise - return expression instanceof JetFunctionLiteralExpression && + //TODO deparenthisise typed + JetExpression deparenthesize = JetPsiUtil.deparenthesize(expression); + return deparenthesize instanceof JetFunctionLiteralExpression && !InlineUtil.hasNoinlineAnnotation(valueParameterDescriptora); } - public void rememberClosure(JetFunctionLiteralExpression expression, Type type) { + public void rememberClosure(JetExpression expression, Type type) { + JetFunctionLiteralExpression lambda = (JetFunctionLiteralExpression) JetPsiUtil.deparenthesize(expression); + assert lambda != null : "Couldn't find lambda in " + expression.getText(); + + String labelNameIfPresent = null; + PsiElement parent = lambda.getParent(); + if (parent instanceof JetLabeledExpression) { + labelNameIfPresent = ((JetLabeledExpression) parent).getLabelName(); + } + LambdaInfo info = new LambdaInfo(lambda, typeMapper, labelNameIfPresent); + ParameterInfo closureInfo = invocationParamBuilder.addNextParameter(type, true, null); - - LambdaInfo info = new LambdaInfo(expression, typeMapper); - expressionMap.put(closureInfo.getIndex(), info); - closureInfo.setLambda(info); + expressionMap.put(closureInfo.getIndex(), info); } private void putClosureParametersOnStack() { for (LambdaInfo next : expressionMap.values()) { - if (next.closure != null) { - activeLambda = next; - codegen.pushClosureOnStack(next.closure, false, this); - } + activeLambda = next; + codegen.pushClosureOnStack(next.closure, false, this); } activeLambda = null; } @@ -435,7 +468,7 @@ public class InlineCodegen implements CallGenerator { ) { //TODO deparenthisise if (isInliningClosure(argumentExpression, valueParameterDescriptor)) { - rememberClosure((JetFunctionLiteralExpression) argumentExpression, parameterType); + rememberClosure(argumentExpression, parameterType); } else { StackValue value = codegen.gen(argumentExpression); putValueIfNeeded(valueParameterDescriptor, parameterType, value); @@ -459,4 +492,22 @@ public class InlineCodegen implements CallGenerator { } putCapturedInLocal(stackValue.type, stackValue, null, paramIndex); } + + + public void generateAndInsertFinallyBlocks(MethodNode intoNode, List insertPoints) { + if (!codegen.hasFinallyBLocks()) return; + + for (MethodInliner.FinallyBlockInfo insertPoint : insertPoints) { + MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode(); + ExpressionCodegen finallyCodegen = + new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(), + codegen.getContext(), codegen.getState(), codegen.getParentCodegen()); + finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements()); + + finallyCodegen.generateFinallyBlocksIfNeeded(insertPoint.returnType); + + InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, insertPoint.beforeIns); + } + } + } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegenUtil.java index f0b66aed17c..5794acc3b8c 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegenUtil.java @@ -43,11 +43,15 @@ import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileFinder; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.org.objectweb.asm.*; +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode; +import org.jetbrains.org.objectweb.asm.tree.InsnList; import org.jetbrains.org.objectweb.asm.tree.MethodNode; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.ListIterator; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait; @@ -63,6 +67,10 @@ public class InlineCodegenUtil { public static final String RECEIVER$0 = "receiver$0"; + public static final String NON_LOCAL_RETURN = "$$$$$NON_LOCAL_RETURN$$$$$"; + + public static final String ROOT_LABEL = "$$$$$ROOT$$$$$"; + @Nullable public static MethodNode getMethodNode( InputStream classData, @@ -74,7 +82,7 @@ public class InlineCodegenUtil { cr.accept(new ClassVisitor(API) { @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) { if (methodName.equals(name) && methodDescriptor.equals(desc)) { return methodNode[0] = new MethodNode(access, name, desc, signature, exceptions); } @@ -271,4 +279,37 @@ public class InlineCodegenUtil { THIS$0.equals(fieldName) || RECEIVER$0.equals(fieldName); } + + public static boolean isReturnOpcode(int opcode) { + return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN; + } + + public static void generateGlobalReturnFlag(@NotNull InstructionAdapter iv, @NotNull String labelName) { + iv.invokestatic(NON_LOCAL_RETURN, labelName, "()V", false); + } + + public static Type getReturnType(int opcode) { + switch (opcode) { + case Opcodes.RETURN: return Type.VOID_TYPE; + case Opcodes.IRETURN: return Type.INT_TYPE; + case Opcodes.DRETURN: return Type.DOUBLE_TYPE; + case Opcodes.FRETURN: return Type.FLOAT_TYPE; + case Opcodes.LRETURN: return Type.LONG_TYPE; + default: return Type.getObjectType("object"); + } + } + + public static void insertNodeBefore(@NotNull MethodNode from, @NotNull MethodNode to, @NotNull AbstractInsnNode afterNode) { + InsnList instructions = to.instructions; + ListIterator iterator = from.instructions.iterator(); + while (iterator.hasNext()) { + AbstractInsnNode next = iterator.next(); + instructions.insertBefore(afterNode, next); + } + } + + + public static MethodNode createEmptyMethodNode() { + return new MethodNode(API, 0, "fake", "()V", null, null); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InliningInstructionAdapter.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InliningInstructionAdapter.java new file mode 100644 index 00000000000..74252804e35 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InliningInstructionAdapter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2014 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.jet.codegen.inline; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.org.objectweb.asm.AnnotationVisitor; +import org.jetbrains.org.objectweb.asm.MethodVisitor; +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; + +public class InliningInstructionAdapter extends InstructionAdapter { + + public InliningInstructionAdapter(MethodVisitor mv) { + super(InlineCodegenUtil.API, mv); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + return null; + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + + } + + @Override + public AnnotationVisitor visitAnnotation(@NotNull String desc, boolean visible) { + return null; + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, @NotNull String desc, boolean visible) { + return null; + } + +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/LabelOwner.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LabelOwner.java new file mode 100644 index 00000000000..5b8a6fad30a --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LabelOwner.java @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2014 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.jet.codegen.inline; + +import org.jetbrains.annotations.NotNull; + +public interface LabelOwner { + + boolean isMyLabel(@NotNull String name); + + LabelOwner SKIP_ALL = new LabelOwner() { + @Override + public boolean isMyLabel(@NotNull String name) { + return false; + } + }; + + + LabelOwner NOT_APPLICABLE = new LabelOwner() { + @Override + public boolean isMyLabel(@NotNull String name) { + throw new RuntimeException("This operation not applicable for current context"); + } + }; + +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaInfo.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaInfo.java index 513a9261e0c..68c42943282 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaInfo.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaInfo.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.codegen.inline; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode; import org.jetbrains.org.objectweb.asm.tree.MethodNode; @@ -38,13 +39,16 @@ import static org.jetbrains.jet.codegen.binding.CodegenBinding.CLOSURE; import static org.jetbrains.jet.codegen.binding.CodegenBinding.anonymousClassForFunction; import static org.jetbrains.jet.codegen.binding.CodegenBinding.asmTypeForAnonymousClass; -public class LambdaInfo implements CapturedParamOwner { +public class LambdaInfo implements CapturedParamOwner, LabelOwner { public final JetFunctionLiteralExpression expression; @NotNull private final JetTypeMapper typeMapper; + @Nullable + public final String labelName; + public final CalculatedClosure closure; private MethodNode node; @@ -57,9 +61,10 @@ public class LambdaInfo implements CapturedParamOwner { private final Type closureClassType; - LambdaInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper) { + LambdaInfo(@NotNull JetFunctionLiteralExpression expression, @NotNull JetTypeMapper typeMapper, @Nullable String labelName) { this.expression = expression; this.typeMapper = typeMapper; + this.labelName = labelName; BindingContext bindingContext = typeMapper.getBindingContext(); functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression.getFunctionLiteral()); assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText(); @@ -68,6 +73,7 @@ public class LambdaInfo implements CapturedParamOwner { closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor); closure = bindingContext.get(CLOSURE, classDescriptor); + assert closure != null : "Closure for lambda should be not null " + expression.getText(); } public MethodNode getNode() { @@ -96,34 +102,28 @@ public class LambdaInfo implements CapturedParamOwner { public List getCapturedVars() { //lazy initialization cause it would be calculated after object creation - int index = 0; if (capturedVars == null) { capturedVars = new ArrayList(); - assert closure != null : "Closure for lambda should be not null " + expression.getText(); if (closure.getCaptureThis() != null) { EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_THIS_FIELD, null, null, typeMapper.mapType(closure.getCaptureThis())); - capturedVars.add(getCapturedParamInfo(descriptor, index)); - index += descriptor.getType().getSize(); + capturedVars.add(getCapturedParamInfo(descriptor)); } if (closure.getCaptureReceiverType() != null) { EnclosedValueDescriptor descriptor = new EnclosedValueDescriptor(AsmUtil.CAPTURED_RECEIVER_FIELD, null, null, typeMapper.mapType(closure.getCaptureReceiverType())); - capturedVars.add(getCapturedParamInfo(descriptor, index)); - index += descriptor.getType().getSize(); + capturedVars.add(getCapturedParamInfo(descriptor)); } for (EnclosedValueDescriptor descriptor : closure.getCaptureVariables().values()) { - capturedVars.add(getCapturedParamInfo(descriptor, index)); - index += descriptor.getType().getSize(); + capturedVars.add(getCapturedParamInfo(descriptor)); } } return capturedVars; } @NotNull - private CapturedParamDesc getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor, int index) { - //return new CapturedParamInfo(CapturedParamDesc.createDesc(this, descriptor.getFieldName(), descriptor.getType()), false, index, -1); + private CapturedParamDesc getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor) { return CapturedParamDesc.createDesc(this, descriptor.getFieldName(), descriptor.getType()); } @@ -157,4 +157,10 @@ public class LambdaInfo implements CapturedParamOwner { public Type getType() { return closureClassType; } + + @Override + public boolean isMyLabel(@NotNull String name) { + return name.equals(labelName); + } + } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java index 1dc9eef6b10..320bae07a19 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java @@ -34,8 +34,9 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.*; import java.util.*; -import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.isInvokeOnLambda; +import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.getReturnType; import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.isAnonymousConstructorCall; +import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.isInvokeOnLambda; public class MethodInliner { @@ -87,25 +88,25 @@ public class MethodInliner { this.result = InlineResult.create(); } - - public InlineResult doInline(MethodVisitor adapter, LocalVarRemapper remapper) { - return doInline(adapter, remapper, true); - } - public InlineResult doInline( - MethodVisitor adapter, - LocalVarRemapper remapper, - boolean remapReturn + @NotNull MethodVisitor adapter, + @NotNull LocalVarRemapper remapper, + boolean remapReturn, + @NotNull LabelOwner labelOwner ) { //analyze body MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node); + //substitute returns with "goto end" instruction to keep non local returns in lambdas + Label end = new Label(); transformedNode = doInline(transformedNode); removeClosureAssertions(transformedNode); - transformedNode.instructions.resetLabels(); + InsnList instructions = transformedNode.instructions; + instructions.resetLabels(); - Label end = new Label(); - RemapVisitor visitor = new RemapVisitor(adapter, end, remapper, remapReturn, nodeRemapper); + MethodNode resultNode = new MethodNode(InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc, + transformedNode.signature, ArrayUtil.toStringArray(transformedNode.exceptions)); + RemapVisitor visitor = new RemapVisitor(resultNode, remapper, nodeRemapper); try { transformedNode.accept(visitor); } @@ -113,7 +114,11 @@ public class MethodInliner { throw wrapException(e, transformedNode, "couldn't inline method call"); } - visitor.visitLabel(end); + resultNode.visitLabel(end); + processReturns(resultNode, labelOwner, remapReturn, end); + + //flush transformed node to output + resultNode.accept(new InliningInstructionAdapter(adapter)); return result; } @@ -133,7 +138,7 @@ public class MethodInliner { private ConstructorInvocation invocation; @Override - public void anew(Type type) { + public void anew(@NotNull Type type) { if (isAnonymousConstructorCall(type.getInternalName(), "")) { invocation = iterator.next(); @@ -193,7 +198,7 @@ public class MethodInliner { "Lambda inlining " + info.getLambdaClassType().getInternalName()); LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift); - InlineResult lambdaResult = inliner.doInline(this.mv, remapper);//TODO add skipped this and receiver + InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info);//TODO add skipped this and receiver result.addAllClassesToRemove(lambdaResult); //return value boxing/unboxing @@ -275,7 +280,7 @@ public class MethodInliner { } @Override - public void visitLineNumber(int line, Label start) { + public void visitLineNumber(int line, @NotNull Label start) { if(isInliningLambda) { super.visitLineNumber(line, start); } @@ -283,7 +288,7 @@ public class MethodInliner { @Override public void visitLocalVariable( - String name, String desc, String signature, Label start, Label end, int index + @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index ) { if (isInliningLambda) { super.visitLocalVariable(name, desc, signature, start, end, getNewIndex(index)); @@ -303,6 +308,7 @@ public class MethodInliner { node = prepareNode(node); Analyzer analyzer = new Analyzer(new SourceInterpreter()); + Frame[] sources; try { sources = analyzer.analyze("fake", node); @@ -513,7 +519,7 @@ public class MethodInliner { return type; } - + @NotNull public RuntimeException wrapException(@NotNull Exception originalException, @NotNull MethodNode node, @NotNull String errorSuffix) { if (originalException instanceof InlineException) { return new InlineException(errorPrefix + ": " + errorSuffix, originalException); @@ -522,4 +528,60 @@ public class MethodInliner { InlineCodegen.getNodeText(node), originalException); } } + + @NotNull + public static List processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) { + if (!remapReturn) { + return Collections.emptyList(); + } + List result = new ArrayList(); + InsnList instructions = node.instructions; + 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; + } + + if (labelName != null) { + isLocalReturn = labelOwner.isMyLabel(labelName); + //remove global return flag + if (isLocalReturn) { + instructions.remove(previous); + } + } + + if (isLocalReturn && endLabel != null) { + LabelNode labelNode = (LabelNode) endLabel.info; + JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode); + instructions.insert(insnNode, jumpInsnNode); + instructions.remove(insnNode); + insnNode = jumpInsnNode; + } + + //genetate finally block before nonLocalReturn flag/return/goto + result.add(new FinallyBlockInfo(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode()))); + } + insnNode = insnNode.getNext(); + } + return result; + } + + public static class FinallyBlockInfo { + + final AbstractInsnNode beforeIns; + + final Type returnType; + + public FinallyBlockInfo(AbstractInsnNode beforeIns, Type returnType) { + this.beforeIns = beforeIns; + this.returnType = returnType; + } + + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java index 8d98c3957c2..07de6cbcd3b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java @@ -16,51 +16,32 @@ package org.jetbrains.jet.codegen.inline; -import org.jetbrains.org.objectweb.asm.AnnotationVisitor; +import org.jetbrains.annotations.NotNull; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; -import org.jetbrains.org.objectweb.asm.Opcodes; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode; import org.jetbrains.jet.codegen.StackValue; -public class RemapVisitor extends InstructionAdapter { - - private final Label end; +public class RemapVisitor extends InliningInstructionAdapter { private final LocalVarRemapper remapper; - private final boolean remapReturn; - private final FieldRemapper nodeRemapper; private final InstructionAdapter instructionAdapter; protected RemapVisitor( MethodVisitor mv, - Label end, LocalVarRemapper localVarRemapper, - boolean remapReturn, FieldRemapper nodeRemapper ) { - super(InlineCodegenUtil.API, mv); + super(mv); this.instructionAdapter = new InstructionAdapter(mv); - this.end = end; this.remapper = localVarRemapper; - this.remapReturn = remapReturn; this.nodeRemapper = nodeRemapper; } - @Override - public void visitInsn(int opcode) { - if (remapReturn && opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) { - super.visitJumpInsn(Opcodes.GOTO, end); - } - else { - super.visitInsn(opcode); - } - } - @Override public void visitIincInsn(int var, int increment) { remapper.visitIincInsn(var, increment, mv); @@ -73,13 +54,13 @@ public class RemapVisitor extends InstructionAdapter { @Override public void visitLocalVariable( - String name, String desc, String signature, Label start, Label end, int index + @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index ) { remapper.visitLocalVariable(name, desc, signature, start, end, index, mv); } @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { + public void visitFieldInsn(int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) { if (name.startsWith("$$$")) { if (nodeRemapper instanceof RegeneratedLambdaFieldRemapper || nodeRemapper.isRoot()) { FieldInsnNode fin = new FieldInsnNode(opcode, owner, name, desc); @@ -95,25 +76,4 @@ public class RemapVisitor extends InstructionAdapter { super.visitFieldInsn(opcode, owner, name, desc); } } - - @Override - public AnnotationVisitor visitAnnotationDefault() { - return null; - } - - @Override - public void visitMaxs(int maxStack, int maxLocals) { - - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return null; - } - - @Override - public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { - return null; - } - } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java index a0f5f88c882..b531c2c7c4a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.lang.resolve; import com.google.common.collect.Lists; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -325,4 +326,21 @@ public class BindingContextUtils { VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor; return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar(); } + + @NotNull + public static Pair getLambdaContainingFunction( + @NotNull FunctionDescriptor lambdaDescriptor, + @NotNull BindingContext context + ) { + FunctionDescriptor containingFunctionDescriptor = lambdaDescriptor; + PsiElement containingFunction; + do { + containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class); + containingFunction = containingFunctionDescriptor != null ? callableDescriptorToDeclaration(context, + containingFunctionDescriptor) : null; + } while (containingFunction instanceof JetFunctionLiteral); + + return new Pair(containingFunctionDescriptor, containingFunction); + } + } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java index df43995bb2e..a2fa031e03b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.lang.types.expressions; import com.google.common.collect.Lists; +import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; @@ -459,10 +460,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { PsiElement containingFunction = BindingContextUtils.callableDescriptorToDeclaration(context.trace.getBindingContext(), containingFunctionDescriptor); assert containingFunction != null; if (containingFunction instanceof JetFunctionLiteral) { - do { - containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class); - containingFunction = containingFunctionDescriptor != null ? BindingContextUtils.callableDescriptorToDeclaration(context.trace.getBindingContext(), containingFunctionDescriptor) : null; - } while (containingFunction instanceof JetFunctionLiteral); + Pair result = BindingContextUtils + .getLambdaContainingFunction(containingFunctionDescriptor, context.trace.getBindingContext()); + containingFunctionDescriptor = result.getFirst(); + containingFunction = result.getSecond(); + // Unqualified, in a function literal context.trace.report(RETURN_NOT_ALLOWED.on(expression)); resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.1.kt new file mode 100644 index 00000000000..fa5bbf7c55b --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.1.kt @@ -0,0 +1,30 @@ +import test.* + +fun test1(b: Boolean): String { + val localResult = doCall ((@local { + () : String -> + if (b) { + return@local "local" + } else { + return "nonLocal" + } + })) + + return "result=" + localResult; +} + +fun test2(nonLocal: String): String { + val localResult = doCall { + return nonLocal + } +} + +fun box(): String { + val test1 = test1(true) + if (test1 != "result=local") return "test1: ${test1}" + + val test2 = test1(false) + if (test2 != "nonLocal") return "test2: ${test2}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.1.kt new file mode 100644 index 00000000000..0e1e774c3c9 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.1.kt @@ -0,0 +1,30 @@ +import test.* + +fun test1(b: Boolean): String { + val localResult = doCall @local { + () : String -> + if (b) { + return@local "local" + } else { + return "nonLocal" + } + } + + return "localResult=" + localResult; +} + +fun test2(nonLocal: String): String { + val localResult = doCall { + return nonLocal + } +} + +fun box(): String { + val test1 = test1(true) + if (test1 != "localResult=local") return "test1: ${test1}" + + val test2 = test1(false) + if (test2 != "nonLocal") return "test2: ${test2}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.1.kt new file mode 100644 index 00000000000..0e1e774c3c9 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.1.kt @@ -0,0 +1,30 @@ +import test.* + +fun test1(b: Boolean): String { + val localResult = doCall @local { + () : String -> + if (b) { + return@local "local" + } else { + return "nonLocal" + } + } + + return "localResult=" + localResult; +} + +fun test2(nonLocal: String): String { + val localResult = doCall { + return nonLocal + } +} + +fun box(): String { + val test1 = test1(true) + if (test1 != "localResult=local") return "test1: ${test1}" + + val test2 = test1(false) + if (test2 != "nonLocal") return "test2: ${test2}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.1.kt new file mode 100644 index 00000000000..67fc6cde5d7 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.1.kt @@ -0,0 +1,17 @@ +import test.* + +class Z {} + +fun test1(nonLocal: String): String { + + val localResult = doCall { + return nonLocal + } +} + +fun box(): String { + val test2 = test1("OK_NONLOCAL") + if (test2 != "OK_NONLOCAL") return "test2: ${test2}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.1.kt new file mode 100644 index 00000000000..7cae382c60f --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.1.kt @@ -0,0 +1,73 @@ +import test.* +import Kind.* + +enum class Kind { + LOCAL + EXTERNAL + GLOBAL +} + +class Internal(val value: String) + +class External(val value: String) + +class Global(val value: String) + +fun test1(intKind: Kind, extKind: Kind): Global { + + var externalResult = doCall @ext { + () : External -> + + val internalResult = doCall @int { + () : Internal -> + if (intKind == Kind.GLOBAL) { + return@test1 Global("internal -> global") + } else if (intKind == EXTERNAL) { + return@ext External("internal -> external") + } + return@int Internal("internal -> local") + } + + if (extKind == GLOBAL || extKind == EXTERNAL) { + return Global("external -> global") + } + + External(internalResult.value + ": external -> local"); + } + + return Global(externalResult.value + ": exit") +} + +fun box(): String { + var test1 = test1(LOCAL, LOCAL).value + if (test1 != "internal -> local: external -> local: exit") return "test1: ${test1}" + + test1 = test1(EXTERNAL, LOCAL).value + if (test1 != "internal -> external: exit") return "test2: ${test1}" + + test1 = test1(GLOBAL, LOCAL).value + if (test1 != "internal -> global") return "test3: ${test1}" + + + test1 = test1(LOCAL, EXTERNAL).value + if (test1 != "external -> global") return "test4: ${test1}" + + test1 = test1(EXTERNAL, EXTERNAL).value + if (test1 != "internal -> external: exit") return "test5: ${test1}" + + test1 = test1(GLOBAL, EXTERNAL).value + if (test1 != "internal -> global") return "test6: ${test1}" + + + test1 = test1(LOCAL, GLOBAL).value + if (test1 != "external -> global") return "test7: ${test1}" + + test1 = test1(EXTERNAL, GLOBAL).value + if (test1 != "internal -> external: exit") return "test8: ${test1}" + + test1 = test1(GLOBAL, GLOBAL).value + if (test1 != "internal -> global") return "test9: ${test1}" + + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.1.kt new file mode 100644 index 00000000000..42195e379df --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.1.kt @@ -0,0 +1,25 @@ +import test.* + +fun test1(b: Boolean): String { + val localResult = noInlineCall @local { + () : Int -> + if (b) { + return@local 1 + } else { + return@local 2 + } + 3 + } + + return "result=" + localResult; +} + +fun box(): String { + val test1 = test1(true) + if (test1 != "result=1") return "test1: ${test1}" + + val test2 = test1(false) + if (test2 != "result=2") return "test2: ${test2}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.2.kt new file mode 100644 index 00000000000..037b6ef9298 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.2.kt @@ -0,0 +1,9 @@ +package test + +public fun noInlineCall(block: ()-> R) : R { + return block() +} + +public inline fun notUsed(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.1.kt new file mode 100644 index 00000000000..5ee99f4f183 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.1.kt @@ -0,0 +1,30 @@ +import test.* + +class A { + var result = 0; + + var field: Int + get() { + doCall { return 1 } + return 2 + } + set(v: Int) { + doCall { + result = v / 2 + return + } + result = v + } +} + + +fun box(): String { + + val a = A() + if (a.field != 1) return "fail 1: ${a.field}" + + a.field = 4 + if (a.result != 2) return "fail 2: ${a.result}" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.2.kt new file mode 100644 index 00000000000..4a5a4a8a634 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.2.kt @@ -0,0 +1,5 @@ +package test + +inline fun doCall(p: () -> R) { + p() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/simple.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/simple.1.kt new file mode 100644 index 00000000000..0d3c9e88492 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/simple.1.kt @@ -0,0 +1,51 @@ +import test.* + +fun test1(local: Int, nonLocal: String, doNonLocal: Boolean): String { + + val localResult = doCall { + if (doNonLocal) { + return nonLocal + } + local + } + + if (localResult == 11) { + return "OK_LOCAL" + } + else { + return "LOCAL_FAILED" + } +} + +fun test2(local: Int, nonLocal: String, doNonLocal: Boolean): String { + + val localResult = doCall { + if (doNonLocal) { + return@test2 nonLocal + } + local + } + + if (localResult == 11) { + return "OK_LOCAL" + } + else { + return "LOCAL_FAILED" + } +} + +fun box(): String { + var test1 = test1(11, "fail", false) + if (test1 != "OK_LOCAL") return "test1: ${test1}" + + test1 = test1(-1, "OK_NONLOCAL", true) + if (test1 != "OK_NONLOCAL") return "test2: ${test1}" + + var test2 = test2(11, "fail", false) + if (test2 != "OK_LOCAL") return "test1: ${test2}" + + test2 = test2(-1, "OK_NONLOCAL", true) + if (test2 != "OK_NONLOCAL") return "test2: ${test2}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/simple.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/simple.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/simple.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.1.kt new file mode 100644 index 00000000000..c4267c88f86 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.1.kt @@ -0,0 +1,30 @@ +import test.* + +class Holder(var value: Int) + +fun test1(holder: Holder, doNonLocal: Boolean) { + holder.value = -1; + + val localResult = doCall { + if (doNonLocal) { + holder.value = 1000 + return + } + 10 + } + + holder.value = localResult +} + + +fun box(): String { + val h = Holder(-1) + + test1(h, false) + if (h.value != 10) return "test1: ${h.value}" + + test1(h, true) + if (h.value != 1000) return "test2: ${h.value}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.1.kt new file mode 100644 index 00000000000..c6b09d75c32 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.1.kt @@ -0,0 +1,100 @@ +import test.* +import Kind.* + +enum class Kind { + LOCAL + EXTERNAL + GLOBAL +} + +class Holder { + var value: String = "" +} + +val FINALLY_CHAIN = "in local finally, in external finally, in global finally" + +class Internal(val value: String) + +class External(val value: String) + +class Global(val value: String) + +fun test1(intKind: Kind, extKind: Kind, holder: Holder): Global { + holder.value = "" + try { + var externalResult = doCall @ext { + (): External -> + + try { + val internalResult = doCall @int { + (): Internal -> + try { + if (intKind == Kind.GLOBAL) { + return@test1 Global("internal -> global") + } + else if (intKind == EXTERNAL) { + return@ext External("internal -> external") + } + return@int Internal("internal -> local") + } + finally { + holder.value += "in local finally" + } + } + + if (extKind == GLOBAL || extKind == EXTERNAL) { + return Global("external -> global") + } + + External(internalResult.value + ": external -> local"); + + } + finally { + holder.value += ", in external finally" + } + } + + return Global(externalResult.value + ": exit") + } + finally { + holder.value += ", in global finally" + } + + +} + +fun box(): String { + var holder = Holder() + + var test1 = test1(LOCAL, LOCAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "internal -> local: external -> local: exit") return "test1: ${test1}, finally = ${holder.value}" + + test1 = test1(EXTERNAL, LOCAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "internal -> external: exit") return "test2: ${test1}, finally = ${holder.value}" + + test1 = test1(GLOBAL, LOCAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "internal -> global") return "test3: ${test1}, finally = ${holder.value}" + + + test1 = test1(LOCAL, EXTERNAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "external -> global") return "test4: ${test1}, finally = ${holder.value}" + + test1 = test1(EXTERNAL, EXTERNAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "internal -> external: exit") return "test5: ${test1}, finally = ${holder.value}" + + test1 = test1(GLOBAL, EXTERNAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "internal -> global") return "test6: ${test1}, finally = ${holder.value}" + + + test1 = test1(LOCAL, GLOBAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "external -> global") return "test7: ${test1}, finally = ${holder.value}" + + test1 = test1(EXTERNAL, GLOBAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "internal -> external: exit") return "test8: ${test1}, finally = ${holder.value}" + + test1 = test1(GLOBAL, GLOBAL, holder).value + if (holder.value != FINALLY_CHAIN || test1 != "internal -> global") return "test9: ${test1}, finally = ${holder.value}" + + + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.2.kt new file mode 100644 index 00000000000..12d90f9709d --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.2.kt @@ -0,0 +1,5 @@ +package test + +public inline fun doCall(block: ()-> R) : R { + return block() +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/AbstractBlackBoxCodegenTest.java b/compiler/tests/org/jetbrains/jet/codegen/generated/AbstractBlackBoxCodegenTest.java index 81ca6691ef7..9ed5756cf4b 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/AbstractBlackBoxCodegenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/AbstractBlackBoxCodegenTest.java @@ -90,12 +90,12 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase { } public void doTestMultiFileWithInlineCheck(@NotNull String firstFileName) { - firstFileName = firstFileName.substring("compiler/testData/codegen/".length()); - List ifiles = new ArrayList(2); - ifiles.add(firstFileName); - ifiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt"); + firstFileName = relativePath(new File(firstFileName)); + List inputFiles = new ArrayList(2); + inputFiles.add(firstFileName); + inputFiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt"); - doTestMultiFile(ifiles); + doTestMultiFile(inputFiles); InlineTestUtil.checkNoCallsToInline(initializedClassLoader.getAllGeneratedFiles()); } diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java index bf33fe5331b..2d9bcea3b7a 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java @@ -31,7 +31,7 @@ import org.jetbrains.jet.codegen.generated.AbstractBlackBoxCodegenTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/boxInline") -@InnerTestClasses({BlackBoxInlineCodegenTestGenerated.AnonymousObject.class, BlackBoxInlineCodegenTestGenerated.Builders.class, BlackBoxInlineCodegenTestGenerated.Capture.class, BlackBoxInlineCodegenTestGenerated.Complex.class, BlackBoxInlineCodegenTestGenerated.DefaultValues.class, BlackBoxInlineCodegenTestGenerated.LambdaClassClash.class, BlackBoxInlineCodegenTestGenerated.LambdaTransformation.class, BlackBoxInlineCodegenTestGenerated.LocalFunInLambda.class, BlackBoxInlineCodegenTestGenerated.NoInline.class, BlackBoxInlineCodegenTestGenerated.Simple.class, BlackBoxInlineCodegenTestGenerated.Special.class, BlackBoxInlineCodegenTestGenerated.Trait.class, BlackBoxInlineCodegenTestGenerated.TryCatchFinally.class}) +@InnerTestClasses({BlackBoxInlineCodegenTestGenerated.AnonymousObject.class, BlackBoxInlineCodegenTestGenerated.Builders.class, BlackBoxInlineCodegenTestGenerated.Capture.class, BlackBoxInlineCodegenTestGenerated.Complex.class, BlackBoxInlineCodegenTestGenerated.DefaultValues.class, BlackBoxInlineCodegenTestGenerated.LambdaClassClash.class, BlackBoxInlineCodegenTestGenerated.LambdaTransformation.class, BlackBoxInlineCodegenTestGenerated.LocalFunInLambda.class, BlackBoxInlineCodegenTestGenerated.NoInline.class, BlackBoxInlineCodegenTestGenerated.NonLocalReturns.class, BlackBoxInlineCodegenTestGenerated.Simple.class, BlackBoxInlineCodegenTestGenerated.Special.class, BlackBoxInlineCodegenTestGenerated.Trait.class, BlackBoxInlineCodegenTestGenerated.TryCatchFinally.class}) public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInBoxInline() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline"), Pattern.compile("^(.+)\\.1.kt$"), true); @@ -269,6 +269,88 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT } + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns") + @InnerTestClasses({NonLocalReturns.Deparenthesize.class, NonLocalReturns.TryFinally.class}) + public static class NonLocalReturns extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInNonLocalReturns() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline/nonLocalReturns"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("explicitLocalReturn.1.kt") + public void testExplicitLocalReturn() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.1.kt"); + } + + @TestMetadata("justReturnInLambda.1.kt") + public void testJustReturnInLambda() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.1.kt"); + } + + @TestMetadata("nestedNonLocals.1.kt") + public void testNestedNonLocals() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.1.kt"); + } + + @TestMetadata("noInlineLocalReturn.1.kt") + public void testNoInlineLocalReturn() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.1.kt"); + } + + @TestMetadata("propertyAccessors.1.kt") + public void testPropertyAccessors() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.1.kt"); + } + + @TestMetadata("simple.1.kt") + public void testSimple() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/simple.1.kt"); + } + + @TestMetadata("simpleVoid.1.kt") + public void testSimpleVoid() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.1.kt"); + } + + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize") + public static class Deparenthesize extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInDeparenthesize() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("bracket.1.kt") + public void testBracket() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.1.kt"); + } + + @TestMetadata("labeled.1.kt") + public void testLabeled() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.1.kt"); + } + + } + + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally") + public static class TryFinally extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInTryFinally() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("external.1.kt") + public void testExternal() throws Exception { + doTestMultiFileWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.1.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("NonLocalReturns"); + suite.addTestSuite(NonLocalReturns.class); + suite.addTestSuite(Deparenthesize.class); + suite.addTestSuite(TryFinally.class); + return suite; + } + } + @TestMetadata("compiler/testData/codegen/boxInline/simple") public static class Simple extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInSimple() throws Exception { @@ -428,6 +510,7 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT suite.addTestSuite(LambdaTransformation.class); suite.addTestSuite(LocalFunInLambda.class); suite.addTestSuite(NoInline.class); + suite.addTest(NonLocalReturns.innerSuite()); suite.addTestSuite(Simple.class); suite.addTestSuite(Special.class); suite.addTestSuite(Trait.class); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java index 0ef2e13b7f2..46e25fd432d 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java @@ -65,11 +65,11 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends TestCaseWit } public void doBoxTestWithInlineCheck(@NotNull String firstFileName) throws Exception { - List ifiles = new ArrayList(2); - ifiles.add(firstFileName); - ifiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt"); + List inputFiles = new ArrayList(2); + inputFiles.add(firstFileName); + inputFiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt"); - ArrayList files = doBoxTest(ifiles); + ArrayList files = doBoxTest(inputFiles); InlineTestUtil.checkNoCallsToInline(files); } diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java index 26959cb903e..4e9e2292382 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -31,7 +31,7 @@ import org.jetbrains.jet.jvm.compiler.AbstractCompileKotlinAgainstKotlinTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/boxInline") -@InnerTestClasses({CompileKotlinAgainstInlineKotlinTestGenerated.AnonymousObject.class, CompileKotlinAgainstInlineKotlinTestGenerated.Builders.class, CompileKotlinAgainstInlineKotlinTestGenerated.Capture.class, CompileKotlinAgainstInlineKotlinTestGenerated.Complex.class, CompileKotlinAgainstInlineKotlinTestGenerated.DefaultValues.class, CompileKotlinAgainstInlineKotlinTestGenerated.LambdaClassClash.class, CompileKotlinAgainstInlineKotlinTestGenerated.LambdaTransformation.class, CompileKotlinAgainstInlineKotlinTestGenerated.LocalFunInLambda.class, CompileKotlinAgainstInlineKotlinTestGenerated.NoInline.class, CompileKotlinAgainstInlineKotlinTestGenerated.Simple.class, CompileKotlinAgainstInlineKotlinTestGenerated.Special.class, CompileKotlinAgainstInlineKotlinTestGenerated.Trait.class, CompileKotlinAgainstInlineKotlinTestGenerated.TryCatchFinally.class}) +@InnerTestClasses({CompileKotlinAgainstInlineKotlinTestGenerated.AnonymousObject.class, CompileKotlinAgainstInlineKotlinTestGenerated.Builders.class, CompileKotlinAgainstInlineKotlinTestGenerated.Capture.class, CompileKotlinAgainstInlineKotlinTestGenerated.Complex.class, CompileKotlinAgainstInlineKotlinTestGenerated.DefaultValues.class, CompileKotlinAgainstInlineKotlinTestGenerated.LambdaClassClash.class, CompileKotlinAgainstInlineKotlinTestGenerated.LambdaTransformation.class, CompileKotlinAgainstInlineKotlinTestGenerated.LocalFunInLambda.class, CompileKotlinAgainstInlineKotlinTestGenerated.NoInline.class, CompileKotlinAgainstInlineKotlinTestGenerated.NonLocalReturns.class, CompileKotlinAgainstInlineKotlinTestGenerated.Simple.class, CompileKotlinAgainstInlineKotlinTestGenerated.Special.class, CompileKotlinAgainstInlineKotlinTestGenerated.Trait.class, CompileKotlinAgainstInlineKotlinTestGenerated.TryCatchFinally.class}) public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompileKotlinAgainstKotlinTest { public void testAllFilesPresentInBoxInline() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline"), Pattern.compile("^(.+)\\.1.kt$"), true); @@ -269,6 +269,88 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi } + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns") + @InnerTestClasses({NonLocalReturns.Deparenthesize.class, NonLocalReturns.TryFinally.class}) + public static class NonLocalReturns extends AbstractCompileKotlinAgainstKotlinTest { + public void testAllFilesPresentInNonLocalReturns() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline/nonLocalReturns"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("explicitLocalReturn.1.kt") + public void testExplicitLocalReturn() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/explicitLocalReturn.1.kt"); + } + + @TestMetadata("justReturnInLambda.1.kt") + public void testJustReturnInLambda() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/justReturnInLambda.1.kt"); + } + + @TestMetadata("nestedNonLocals.1.kt") + public void testNestedNonLocals() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/nestedNonLocals.1.kt"); + } + + @TestMetadata("noInlineLocalReturn.1.kt") + public void testNoInlineLocalReturn() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/noInlineLocalReturn.1.kt"); + } + + @TestMetadata("propertyAccessors.1.kt") + public void testPropertyAccessors() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/propertyAccessors.1.kt"); + } + + @TestMetadata("simple.1.kt") + public void testSimple() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/simple.1.kt"); + } + + @TestMetadata("simpleVoid.1.kt") + public void testSimpleVoid() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/simpleVoid.1.kt"); + } + + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize") + public static class Deparenthesize extends AbstractCompileKotlinAgainstKotlinTest { + public void testAllFilesPresentInDeparenthesize() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("bracket.1.kt") + public void testBracket() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/bracket.1.kt"); + } + + @TestMetadata("labeled.1.kt") + public void testLabeled() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/deparenthesize/labeled.1.kt"); + } + + } + + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally") + public static class TryFinally extends AbstractCompileKotlinAgainstKotlinTest { + public void testAllFilesPresentInTryFinally() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("external.1.kt") + public void testExternal() throws Exception { + doBoxTestWithInlineCheck("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/external.1.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("NonLocalReturns"); + suite.addTestSuite(NonLocalReturns.class); + suite.addTestSuite(Deparenthesize.class); + suite.addTestSuite(TryFinally.class); + return suite; + } + } + @TestMetadata("compiler/testData/codegen/boxInline/simple") public static class Simple extends AbstractCompileKotlinAgainstKotlinTest { public void testAllFilesPresentInSimple() throws Exception { @@ -428,6 +510,7 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi suite.addTestSuite(LambdaTransformation.class); suite.addTestSuite(LocalFunInLambda.class); suite.addTestSuite(NoInline.class); + suite.addTest(NonLocalReturns.innerSuite()); suite.addTestSuite(Simple.class); suite.addTestSuite(Special.class); suite.addTestSuite(Trait.class);