Support non local return generation, support non local return inlining
This commit is contained in:
@@ -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<StackValue, StackValue> implements LocalLookup {
|
||||
private static final Set<DeclarationDescriptor> INTEGRAL_RANGES = KotlinBuiltIns.getInstance().getIntegralRanges();
|
||||
@@ -1530,7 +1533,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> 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<StackValue, StackValue> 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<BlockStackElement> getBlockStackElements() {
|
||||
return new Stack<BlockStackElement>(blockStackElements);
|
||||
}
|
||||
|
||||
public void addBlockStackElementsForNonLocalReturns(@NotNull Stack<BlockStackElement> elements) {
|
||||
blockStackElements.addAll(elements);
|
||||
}
|
||||
|
||||
private static class NonLocalReturnInfo {
|
||||
|
||||
final Type returnType;
|
||||
|
||||
final String labelName;
|
||||
|
||||
private NonLocalReturnInfo(Type type, String name) {
|
||||
returnType = type;
|
||||
labelName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -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);
|
||||
|
||||
@@ -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<MethodInliner.FinallyBlockInfo> 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<MethodInliner.FinallyBlockInfo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<AbstractInsnNode> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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<CapturedParamDesc> getCapturedVars() {
|
||||
//lazy initialization cause it would be calculated after object creation
|
||||
int index = 0;
|
||||
if (capturedVars == null) {
|
||||
capturedVars = new ArrayList<CapturedParamDesc>();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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(), "<init>")) {
|
||||
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<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter());
|
||||
|
||||
Frame<SourceValue>[] 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<FinallyBlockInfo> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) {
|
||||
if (!remapReturn) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<FinallyBlockInfo> result = new ArrayList<FinallyBlockInfo>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<FunctionDescriptor, PsiElement> 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<FunctionDescriptor, PsiElement>(containingFunctionDescriptor, containingFunction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-4
@@ -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<FunctionDescriptor, PsiElement> 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);
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
public fun <R> noInlineCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
|
||||
public inline fun <R> notUsed(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
inline fun <R> doCall(p: () -> R) {
|
||||
p()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
public inline fun <R> doCall(block: ()-> R) : R {
|
||||
return block()
|
||||
}
|
||||
@@ -90,12 +90,12 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
|
||||
}
|
||||
|
||||
public void doTestMultiFileWithInlineCheck(@NotNull String firstFileName) {
|
||||
firstFileName = firstFileName.substring("compiler/testData/codegen/".length());
|
||||
List<String> ifiles = new ArrayList<String>(2);
|
||||
ifiles.add(firstFileName);
|
||||
ifiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt");
|
||||
firstFileName = relativePath(new File(firstFileName));
|
||||
List<String> inputFiles = new ArrayList<String>(2);
|
||||
inputFiles.add(firstFileName);
|
||||
inputFiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt");
|
||||
|
||||
doTestMultiFile(ifiles);
|
||||
doTestMultiFile(inputFiles);
|
||||
InlineTestUtil.checkNoCallsToInline(initializedClassLoader.getAllGeneratedFiles());
|
||||
}
|
||||
|
||||
|
||||
+84
-1
@@ -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);
|
||||
|
||||
+4
-4
@@ -65,11 +65,11 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends TestCaseWit
|
||||
}
|
||||
|
||||
public void doBoxTestWithInlineCheck(@NotNull String firstFileName) throws Exception {
|
||||
List<String> ifiles = new ArrayList<String>(2);
|
||||
ifiles.add(firstFileName);
|
||||
ifiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt");
|
||||
List<String> inputFiles = new ArrayList<String>(2);
|
||||
inputFiles.add(firstFileName);
|
||||
inputFiles.add(firstFileName.substring(0, firstFileName.length() - "1.kt".length()) + "2.kt");
|
||||
|
||||
ArrayList<OutputFile> files = doBoxTest(ifiles);
|
||||
ArrayList<OutputFile> files = doBoxTest(inputFiles);
|
||||
InlineTestUtil.checkNoCallsToInline(files);
|
||||
}
|
||||
|
||||
|
||||
+84
-1
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user