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.context;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.kotlin.codegen.ExpressionCodegen;
021    import org.jetbrains.kotlin.codegen.JvmCodegenUtil;
022    import org.jetbrains.kotlin.codegen.StackValue;
023    import org.jetbrains.kotlin.codegen.binding.MutableClosure;
024    import org.jetbrains.kotlin.codegen.state.GenerationState;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.load.java.JvmAbi;
027    import org.jetbrains.kotlin.resolve.BindingContext;
028    import org.jetbrains.kotlin.types.JetType;
029    import org.jetbrains.org.objectweb.asm.Type;
030    
031    import static org.jetbrains.kotlin.codegen.AsmUtil.CAPTURED_RECEIVER_FIELD;
032    import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
033    
034    public interface LocalLookup {
035        boolean lookupLocal(DeclarationDescriptor descriptor);
036    
037        enum LocalLookupCase {
038            VAR {
039                @Override
040                public boolean isCase(DeclarationDescriptor d) {
041                    return d instanceof VariableDescriptor && !(d instanceof PropertyDescriptor);
042                }
043    
044                @Override
045                public StackValue.StackValueWithSimpleReceiver innerValue(
046                        DeclarationDescriptor d,
047                        LocalLookup localLookup,
048                        GenerationState state,
049                        MutableClosure closure,
050                        Type classType
051                ) {
052                    VariableDescriptor vd = (VariableDescriptor) d;
053    
054                    boolean idx = localLookup != null && localLookup.lookupLocal(vd);
055                    if (!idx) return null;
056    
057                    Type sharedVarType = state.getTypeMapper().getSharedVarType(vd);
058                    Type localType = state.getTypeMapper().mapType(vd);
059                    Type type = sharedVarType != null ? sharedVarType : localType;
060    
061                    String fieldName = "$" + vd.getName();
062                    StackValue.Local thiz = StackValue.LOCAL_0;
063                    StackValue.StackValueWithSimpleReceiver innerValue = sharedVarType != null
064                                            ? StackValue.fieldForSharedVar(localType, classType, fieldName, thiz)
065                                            : StackValue.field(type, classType, fieldName, false, thiz);
066    
067                    closure.recordField(fieldName, type);
068                    closure.captureVariable(new EnclosedValueDescriptor(fieldName, d, innerValue, type));
069    
070                    return innerValue;
071                }
072            },
073    
074            LOCAL_NAMED_FUNCTION {
075                @Override
076                public boolean isCase(DeclarationDescriptor d) {
077                    return isLocalNamedFun(d);
078                }
079    
080                @Override
081                public StackValue.StackValueWithSimpleReceiver innerValue(
082                        DeclarationDescriptor d,
083                        LocalLookup localLookup,
084                        GenerationState state,
085                        MutableClosure closure,
086                        Type classType
087                ) {
088                    FunctionDescriptor vd = (FunctionDescriptor) d;
089    
090                    boolean idx = localLookup != null && localLookup.lookupLocal(vd);
091                    if (!idx) return null;
092    
093                    BindingContext bindingContext = state.getBindingContext();
094                    Type localType = asmTypeForAnonymousClass(bindingContext, vd);
095    
096                    MutableClosure localFunClosure = bindingContext.get(CLOSURE, bindingContext.get(CLASS_FOR_FUNCTION, vd));
097                    if (localFunClosure != null && JvmCodegenUtil.isConst(localFunClosure)) {
098                        // This is an optimization: we can obtain an instance of a const closure simply by GETSTATIC ...$instance
099                        // (instead of passing this instance to the constructor and storing as a field)
100                        return StackValue.field(localType, localType, JvmAbi.INSTANCE_FIELD, true, StackValue.LOCAL_0);
101                    }
102    
103                    String fieldName = "$" + vd.getName();
104                    StackValue.StackValueWithSimpleReceiver innerValue = StackValue.field(localType, classType, fieldName, false,
105                                                                                          StackValue.LOCAL_0);
106    
107                    closure.recordField(fieldName, localType);
108                    closure.captureVariable(new EnclosedValueDescriptor(fieldName, d, innerValue, localType));
109    
110                    return innerValue;
111                }
112            },
113    
114            RECEIVER {
115                @Override
116                public boolean isCase(DeclarationDescriptor d) {
117                    return d instanceof ReceiverParameterDescriptor;
118                }
119    
120                @Override
121                public StackValue.StackValueWithSimpleReceiver innerValue(
122                        DeclarationDescriptor d,
123                        LocalLookup enclosingLocalLookup,
124                        GenerationState state,
125                        MutableClosure closure,
126                        Type classType
127                ) {
128                    if (closure.getEnclosingReceiverDescriptor() != d) {
129                        return null;
130                    }
131    
132                    JetType receiverType = closure.getEnclosingReceiverDescriptor().getType();
133                    Type type = state.getTypeMapper().mapType(receiverType);
134                    StackValue.StackValueWithSimpleReceiver innerValue = StackValue.field(type, classType, CAPTURED_RECEIVER_FIELD, false,
135                                                                                          StackValue.LOCAL_0);
136                    closure.setCaptureReceiver();
137    
138                    return innerValue;
139                }
140    
141                @NotNull
142                @Override
143                public StackValue outerValue(@NotNull EnclosedValueDescriptor d, @NotNull ExpressionCodegen codegen) {
144                    CallableDescriptor descriptor = (CallableDescriptor) d.getDescriptor();
145                    return StackValue.local(descriptor.getDispatchReceiverParameter() != null ? 1 : 0, d.getType());
146                }
147            };
148    
149            public abstract boolean isCase(DeclarationDescriptor d);
150    
151            public abstract StackValue.StackValueWithSimpleReceiver innerValue(
152                    DeclarationDescriptor d,
153                    LocalLookup localLookup,
154                    GenerationState state,
155                    MutableClosure closure,
156                    Type classType
157            );
158    
159            @NotNull
160            public StackValue outerValue(@NotNull EnclosedValueDescriptor d, @NotNull ExpressionCodegen codegen) {
161                int idx = codegen.lookupLocalIndex(d.getDescriptor());
162                assert idx != -1;
163    
164                return StackValue.local(idx, d.getType());
165            }
166        }
167    }