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