001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.codegen.inline; 018 019 import org.jetbrains.annotations.NotNull; 020 import org.jetbrains.kotlin.codegen.AsmUtil; 021 import org.jetbrains.kotlin.codegen.StackValue; 022 import org.jetbrains.kotlin.codegen.binding.CalculatedClosure; 023 import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor; 024 import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; 025 import org.jetbrains.kotlin.descriptors.ClassDescriptor; 026 import org.jetbrains.kotlin.descriptors.FunctionDescriptor; 027 import org.jetbrains.kotlin.psi.KtExpression; 028 import org.jetbrains.kotlin.psi.KtLambdaExpression; 029 import org.jetbrains.kotlin.resolve.BindingContext; 030 import org.jetbrains.kotlin.resolve.jvm.AsmTypes; 031 import org.jetbrains.org.objectweb.asm.Type; 032 import org.jetbrains.org.objectweb.asm.commons.Method; 033 import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode; 034 035 import java.util.ArrayList; 036 import java.util.Arrays; 037 import java.util.List; 038 import java.util.Set; 039 040 import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*; 041 042 public class LambdaInfo implements LabelOwner { 043 public final KtExpression expression; 044 private final KotlinTypeMapper typeMapper; 045 public final Set<String> labels; 046 private final CalculatedClosure closure; 047 public final boolean isCrossInline; 048 private final FunctionDescriptor functionDescriptor; 049 private final ClassDescriptor classDescriptor; 050 private final Type closureClassType; 051 052 private SMAPAndMethodNode node; 053 private List<CapturedParamDesc> capturedVars; 054 055 public LambdaInfo(@NotNull KtExpression expression, @NotNull KotlinTypeMapper typeMapper, boolean isCrossInline) { 056 this.isCrossInline = isCrossInline; 057 this.expression = expression instanceof KtLambdaExpression ? 058 ((KtLambdaExpression) expression).getFunctionLiteral() : expression; 059 060 this.typeMapper = typeMapper; 061 BindingContext bindingContext = typeMapper.getBindingContext(); 062 functionDescriptor = bindingContext.get(BindingContext.FUNCTION, this.expression); 063 assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText(); 064 065 classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor); 066 closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor); 067 068 closure = bindingContext.get(CLOSURE, classDescriptor); 069 assert closure != null : "Closure for lambda should be not null " + expression.getText(); 070 071 labels = InlineCodegen.getDeclarationLabels(expression, functionDescriptor); 072 } 073 074 @NotNull 075 public SMAPAndMethodNode getNode() { 076 return node; 077 } 078 079 public void setNode(@NotNull SMAPAndMethodNode node) { 080 this.node = node; 081 } 082 083 @NotNull 084 public FunctionDescriptor getFunctionDescriptor() { 085 return functionDescriptor; 086 } 087 088 @NotNull 089 public KtExpression getFunctionWithBodyOrCallableReference() { 090 return expression; 091 } 092 093 @NotNull 094 public ClassDescriptor getClassDescriptor() { 095 return classDescriptor; 096 } 097 098 @NotNull 099 public Type getLambdaClassType() { 100 return closureClassType; 101 } 102 103 @NotNull 104 public List<CapturedParamDesc> getCapturedVars() { 105 //lazy initialization cause it would be calculated after object creation 106 if (capturedVars == null) { 107 capturedVars = new ArrayList<CapturedParamDesc>(); 108 109 if (closure.getCaptureThis() != null) { 110 Type type = typeMapper.mapType(closure.getCaptureThis()); 111 EnclosedValueDescriptor descriptor = 112 new EnclosedValueDescriptor( 113 AsmUtil.CAPTURED_THIS_FIELD, 114 /* descriptor = */ null, 115 StackValue.field(type, closureClassType, AsmUtil.CAPTURED_THIS_FIELD, false, StackValue.LOCAL_0), 116 type 117 ); 118 capturedVars.add(getCapturedParamInfo(descriptor)); 119 } 120 121 if (closure.getCaptureReceiverType() != null) { 122 Type type = typeMapper.mapType(closure.getCaptureReceiverType()); 123 EnclosedValueDescriptor descriptor = 124 new EnclosedValueDescriptor( 125 AsmUtil.CAPTURED_RECEIVER_FIELD, 126 /* descriptor = */ null, 127 StackValue.field(type, closureClassType, AsmUtil.CAPTURED_RECEIVER_FIELD, false, StackValue.LOCAL_0), 128 type 129 ); 130 capturedVars.add(getCapturedParamInfo(descriptor)); 131 } 132 133 for (EnclosedValueDescriptor descriptor : closure.getCaptureVariables().values()) { 134 capturedVars.add(getCapturedParamInfo(descriptor)); 135 } 136 } 137 return capturedVars; 138 } 139 140 @NotNull 141 private CapturedParamDesc getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor) { 142 return new CapturedParamDesc(closureClassType, descriptor.getFieldName(), descriptor.getType()); 143 } 144 145 @NotNull 146 public List<Type> getInvokeParamsWithoutCaptured() { 147 return Arrays.asList(typeMapper.mapAsmMethod(functionDescriptor).getArgumentTypes()); 148 } 149 150 @NotNull 151 public Parameters addAllParameters(@NotNull FieldRemapper remapper) { 152 Method asmMethod = typeMapper.mapAsmMethod(getFunctionDescriptor()); 153 ParametersBuilder builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, asmMethod.getDescriptor(), this); 154 155 for (CapturedParamDesc info : getCapturedVars()) { 156 CapturedParamInfo field = remapper.findField(new FieldInsnNode(0, info.getContainingLambdaName(), info.getFieldName(), "")); 157 assert field != null : "Captured field not found: " + info.getContainingLambdaName() + "." + info.getFieldName(); 158 builder.addCapturedParam(field, info.getFieldName()); 159 } 160 161 return builder.buildParameters(); 162 } 163 164 @Override 165 public boolean isMyLabel(@NotNull String name) { 166 return labels.contains(name); 167 } 168 }