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    }