Support non local return generation, support non local return inlining

This commit is contained in:
Michael Bogdanov
2014-06-05 16:56:49 +04:00
parent 02c6bdeaa3
commit 8092717da4
35 changed files with 1057 additions and 121 deletions
@@ -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;
}
}
}
@@ -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);
}
}
@@ -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());
}
@@ -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);
@@ -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);
}
@@ -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);