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.PropertyReferenceCodegen;
022 import org.jetbrains.kotlin.codegen.StackValue;
023 import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
024 import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
025 import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor;
026 import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
027 import org.jetbrains.kotlin.descriptors.*;
028 import org.jetbrains.kotlin.psi.KtCallableReferenceExpression;
029 import org.jetbrains.kotlin.psi.KtExpression;
030 import org.jetbrains.kotlin.psi.KtLambdaExpression;
031 import org.jetbrains.kotlin.resolve.BindingContext;
032 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
033 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
034 import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
035 import org.jetbrains.org.objectweb.asm.Type;
036 import org.jetbrains.org.objectweb.asm.commons.Method;
037 import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
038
039 import java.util.ArrayList;
040 import java.util.Arrays;
041 import java.util.List;
042 import java.util.Set;
043
044 import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
045
046 public class LambdaInfo implements LabelOwner {
047 public final KtExpression expression;
048 private final KotlinTypeMapper typeMapper;
049 public final Set<String> labels;
050 private final CalculatedClosure closure;
051 public final boolean isCrossInline;
052 private final FunctionDescriptor functionDescriptor;
053 private final ClassDescriptor classDescriptor;
054 private final Type closureClassType;
055
056 private SMAPAndMethodNode node;
057 private List<CapturedParamDesc> capturedVars;
058 private final PropertyReferenceInfo propertyReferenceInfo;
059
060 public LambdaInfo(
061 @NotNull KtExpression expression,
062 @NotNull KotlinTypeMapper typeMapper,
063 boolean isCrossInline
064 ) {
065 this.isCrossInline = isCrossInline;
066 this.expression = expression instanceof KtLambdaExpression ?
067 ((KtLambdaExpression) expression).getFunctionLiteral() : expression;
068
069 this.typeMapper = typeMapper;
070 BindingContext bindingContext = typeMapper.getBindingContext();
071 FunctionDescriptor function = bindingContext.get(BindingContext.FUNCTION, this.expression);
072 if (function == null && expression instanceof KtCallableReferenceExpression) {
073 VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, this.expression);
074 assert variableDescriptor != null :
075 "Reference expression not resolved to variable descriptor: " + expression.getText();
076 classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor);
077 closureClassType = typeMapper.mapClass(classDescriptor);
078 SimpleFunctionDescriptor getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor);
079 functionDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor);
080 ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(((KtCallableReferenceExpression) expression).getCallableReference(), bindingContext);
081 propertyReferenceInfo = new PropertyReferenceInfo(
082 (VariableDescriptor) resolvedCall.getResultingDescriptor(), getFunction
083 );
084 }
085 else {
086 propertyReferenceInfo = null;
087 functionDescriptor = function;
088 assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText();
089 classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor);
090 closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor);
091 }
092
093
094 closure = bindingContext.get(CLOSURE, classDescriptor);
095 assert closure != null : "Closure for lambda should be not null " + expression.getText();
096
097 labels = InlineCodegen.getDeclarationLabels(expression, functionDescriptor);
098 }
099
100 @NotNull
101 public SMAPAndMethodNode getNode() {
102 return node;
103 }
104
105 public void setNode(@NotNull SMAPAndMethodNode node) {
106 this.node = node;
107 }
108
109 @NotNull
110 public FunctionDescriptor getFunctionDescriptor() {
111 return functionDescriptor;
112 }
113
114 @NotNull
115 public KtExpression getFunctionWithBodyOrCallableReference() {
116 return expression;
117 }
118
119 @NotNull
120 public ClassDescriptor getClassDescriptor() {
121 return classDescriptor;
122 }
123
124 @NotNull
125 public Type getLambdaClassType() {
126 return closureClassType;
127 }
128
129 @NotNull
130 public List<CapturedParamDesc> getCapturedVars() {
131 //lazy initialization cause it would be calculated after object creation
132 if (capturedVars == null) {
133 capturedVars = new ArrayList<CapturedParamDesc>();
134
135 if (closure.getCaptureThis() != null) {
136 Type type = typeMapper.mapType(closure.getCaptureThis());
137 EnclosedValueDescriptor descriptor =
138 new EnclosedValueDescriptor(
139 AsmUtil.CAPTURED_THIS_FIELD,
140 /* descriptor = */ null,
141 StackValue.field(type, closureClassType, AsmUtil.CAPTURED_THIS_FIELD, false, StackValue.LOCAL_0),
142 type
143 );
144 capturedVars.add(getCapturedParamInfo(descriptor));
145 }
146
147 if (closure.getCaptureReceiverType() != null) {
148 Type type = typeMapper.mapType(closure.getCaptureReceiverType());
149 EnclosedValueDescriptor descriptor =
150 new EnclosedValueDescriptor(
151 AsmUtil.CAPTURED_RECEIVER_FIELD,
152 /* descriptor = */ null,
153 StackValue.field(type, closureClassType, AsmUtil.CAPTURED_RECEIVER_FIELD, false, StackValue.LOCAL_0),
154 type
155 );
156 capturedVars.add(getCapturedParamInfo(descriptor));
157 }
158
159 for (EnclosedValueDescriptor descriptor : closure.getCaptureVariables().values()) {
160 capturedVars.add(getCapturedParamInfo(descriptor));
161 }
162 }
163 return capturedVars;
164 }
165
166 @NotNull
167 private CapturedParamDesc getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor) {
168 return new CapturedParamDesc(closureClassType, descriptor.getFieldName(), descriptor.getType());
169 }
170
171 @NotNull
172 public List<Type> getInvokeParamsWithoutCaptured() {
173 return Arrays.asList(typeMapper.mapAsmMethod(functionDescriptor).getArgumentTypes());
174 }
175
176 @NotNull
177 public Parameters addAllParameters(@NotNull FieldRemapper remapper) {
178 Method asmMethod = typeMapper.mapAsmMethod(getFunctionDescriptor());
179 ParametersBuilder builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, asmMethod.getDescriptor(), this);
180
181 for (CapturedParamDesc info : getCapturedVars()) {
182 CapturedParamInfo field = remapper.findField(new FieldInsnNode(0, info.getContainingLambdaName(), info.getFieldName(), ""));
183 assert field != null : "Captured field not found: " + info.getContainingLambdaName() + "." + info.getFieldName();
184 builder.addCapturedParam(field, info.getFieldName());
185 }
186
187 return builder.buildParameters();
188 }
189
190 @Override
191 public boolean isMyLabel(@NotNull String name) {
192 return labels.contains(name);
193 }
194
195 public boolean isPropertyReference() {
196 return propertyReferenceInfo != null;
197 }
198
199 public PropertyReferenceInfo getPropertyReferenceInfo() {
200 return propertyReferenceInfo;
201 }
202 }