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 kotlin.jvm.functions.Function0;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.codegen.*;
023    import org.jetbrains.kotlin.codegen.binding.MutableClosure;
024    import org.jetbrains.kotlin.codegen.state.GenerationState;
025    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
026    import org.jetbrains.kotlin.descriptors.*;
027    import org.jetbrains.kotlin.resolve.BindingContext;
028    import org.jetbrains.kotlin.resolve.DescriptorUtils;
029    import org.jetbrains.kotlin.storage.LockBasedStorageManager;
030    import org.jetbrains.kotlin.storage.NullableLazyValue;
031    import org.jetbrains.kotlin.types.JetType;
032    import org.jetbrains.org.objectweb.asm.Type;
033    
034    import java.util.*;
035    
036    import static org.jetbrains.kotlin.codegen.AsmUtil.getVisibilityAccessFlag;
037    import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE;
038    import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PROTECTED;
039    
040    public abstract class CodegenContext<T extends DeclarationDescriptor> {
041        public static final CodegenContext STATIC = new RootContext();
042    
043        private final T contextDescriptor;
044        private final OwnerKind contextKind;
045        private final CodegenContext parentContext;
046        private final ClassDescriptor thisDescriptor;
047        public final MutableClosure closure;
048        private final LocalLookup enclosingLocalLookup;
049        private final NullableLazyValue<StackValue.Field> outerExpression;
050    
051        private Map<DeclarationDescriptor, DeclarationDescriptor> accessors;
052        private Map<DeclarationDescriptor, CodegenContext> childContexts;
053    
054        public CodegenContext(
055                @NotNull T contextDescriptor,
056                @NotNull OwnerKind contextKind,
057                @Nullable CodegenContext parentContext,
058                @Nullable MutableClosure closure,
059                @Nullable ClassDescriptor thisDescriptor,
060                @Nullable LocalLookup localLookup
061        ) {
062            this.contextDescriptor = contextDescriptor;
063            this.contextKind = contextKind;
064            this.parentContext = parentContext;
065            this.closure = closure;
066            this.thisDescriptor = thisDescriptor;
067            this.enclosingLocalLookup = localLookup;
068            this.outerExpression = LockBasedStorageManager.NO_LOCKS.createNullableLazyValue(new Function0<StackValue.Field>() {
069                @Override
070                public StackValue.Field invoke() {
071                    return computeOuterExpression();
072                }
073            });
074    
075            if (parentContext != null) {
076                parentContext.addChild(this);
077            }
078        }
079    
080        @NotNull
081        public final ClassDescriptor getThisDescriptor() {
082            if (thisDescriptor == null) {
083                throw new UnsupportedOperationException("Context doesn't have a \"this\": " + this);
084            }
085            return thisDescriptor;
086        }
087    
088        public final boolean hasThisDescriptor() {
089            return thisDescriptor != null;
090        }
091    
092        @NotNull
093        @SuppressWarnings("unchecked")
094        public CodegenContext<? extends ClassOrPackageFragmentDescriptor> getClassOrPackageParentContext() {
095            CodegenContext<?> context = this;
096            while (true) {
097                if (context.getContextDescriptor() instanceof ClassOrPackageFragmentDescriptor) {
098                    return (CodegenContext) context;
099                }
100                context = context.getParentContext();
101                assert context != null : "Context which is not top-level has no parent: " + this;
102            }
103        }
104    
105        /**
106         * This method returns not null only if context descriptor corresponds to method or function which has receiver
107         */
108        @Nullable
109        public final CallableDescriptor getCallableDescriptorWithReceiver() {
110            if (contextDescriptor instanceof CallableDescriptor) {
111                CallableDescriptor callableDescriptor = (CallableDescriptor) getContextDescriptor();
112                return callableDescriptor.getExtensionReceiverParameter() != null ? callableDescriptor : null;
113            }
114            return null;
115        }
116    
117        public StackValue getOuterExpression(@Nullable StackValue prefix, boolean ignoreNoOuter) {
118            return getOuterExpression(prefix, ignoreNoOuter, true);
119        }
120    
121        private StackValue getOuterExpression(@Nullable StackValue prefix, boolean ignoreNoOuter, boolean captureThis) {
122            if (outerExpression.invoke() == null) {
123                if (!ignoreNoOuter) {
124                    throw new UnsupportedOperationException("Don't know how to generate outer expression for " + getContextDescriptor());
125                }
126                return null;
127            }
128            if (captureThis) {
129                if (closure == null) {
130                    throw new IllegalStateException("Can't capture this for context without closure: " + getContextDescriptor());
131                }
132                closure.setCaptureThis();
133            }
134            return StackValue.changeReceiverForFieldAndSharedVar(outerExpression.invoke(), prefix);
135        }
136    
137        @NotNull
138        public T getContextDescriptor() {
139            return contextDescriptor;
140        }
141    
142        @NotNull
143        public OwnerKind getContextKind() {
144            return contextKind;
145        }
146    
147        @NotNull
148        public PackageContext intoPackagePart(@NotNull PackageFragmentDescriptor descriptor, Type packagePartType) {
149            return new PackageContext(descriptor, this, packagePartType);
150        }
151    
152        @NotNull
153        public FieldOwnerContext intoPackageFacade(@NotNull Type delegateTo, @NotNull PackageFragmentDescriptor descriptor) {
154            return new PackageFacadeContext(descriptor, this, delegateTo);
155        }
156    
157        @NotNull
158        public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind, GenerationState state) {
159            return new ClassContext(state.getTypeMapper(), descriptor, kind, this, null);
160        }
161    
162        @NotNull
163        public ClassContext intoAnonymousClass(@NotNull ClassDescriptor descriptor, @NotNull ExpressionCodegen codegen, @NotNull OwnerKind ownerKind) {
164            return new AnonymousClassContext(codegen.getState().getTypeMapper(), descriptor, ownerKind, this, codegen);
165        }
166    
167        @NotNull
168        public MethodContext intoFunction(FunctionDescriptor descriptor) {
169            return new MethodContext(descriptor, getContextKind(), this, null, false);
170        }
171    
172        @NotNull
173        public MethodContext intoInlinedLambda(FunctionDescriptor descriptor) {
174            return new MethodContext(descriptor, getContextKind(), this, null, true);
175        }
176    
177        @NotNull
178        public ConstructorContext intoConstructor(@NotNull ConstructorDescriptor descriptor) {
179            return new ConstructorContext(descriptor, getContextKind(), this, closure);
180        }
181    
182        // SCRIPT: generate into script, move to ScriptingUtil
183        @NotNull
184        public ScriptContext intoScript(
185                @NotNull ScriptDescriptor script,
186                @NotNull List<ScriptDescriptor> earlierScripts,
187                @NotNull ClassDescriptor classDescriptor
188        ) {
189            return new ScriptContext(script, earlierScripts, classDescriptor, OwnerKind.IMPLEMENTATION, this, closure);
190        }
191    
192        @NotNull
193        public ClosureContext intoClosure(
194                @NotNull FunctionDescriptor funDescriptor,
195                @NotNull LocalLookup localLookup,
196                @NotNull JetTypeMapper typeMapper
197        ) {
198            return new ClosureContext(typeMapper, funDescriptor, this, localLookup);
199        }
200    
201        @Nullable
202        public CodegenContext getParentContext() {
203            return parentContext;
204        }
205    
206        public ClassDescriptor getEnclosingClass() {
207            CodegenContext cur = getParentContext();
208            while (cur != null && !(cur.getContextDescriptor() instanceof ClassDescriptor)) {
209                cur = cur.getParentContext();
210            }
211    
212            return cur == null ? null : (ClassDescriptor) cur.getContextDescriptor();
213        }
214    
215        @Nullable
216        public CodegenContext findParentContextWithDescriptor(DeclarationDescriptor descriptor) {
217            CodegenContext c = this;
218            while (c != null) {
219                if (c.getContextDescriptor() == descriptor) break;
220                c = c.getParentContext();
221            }
222            return c;
223        }
224    
225        @NotNull
226        public DeclarationDescriptor getAccessor(@NotNull DeclarationDescriptor descriptor) {
227            return getAccessor(descriptor, false, null);
228        }
229    
230        @NotNull
231        public DeclarationDescriptor getAccessor(@NotNull DeclarationDescriptor descriptor, boolean isForBackingFieldInOuterClass, @Nullable JetType delegateType) {
232            if (accessors == null) {
233                accessors = new LinkedHashMap<DeclarationDescriptor, DeclarationDescriptor>();
234            }
235            descriptor = descriptor.getOriginal();
236            DeclarationDescriptor accessor = accessors.get(descriptor);
237            if (accessor != null) {
238                assert !isForBackingFieldInOuterClass ||
239                       accessor instanceof AccessorForPropertyBackingFieldInOuterClass : "There is already exists accessor with isForBackingFieldInOuterClass = false in this context";
240                return accessor;
241            }
242    
243            int accessorIndex = accessors.size();
244            if (descriptor instanceof SimpleFunctionDescriptor || descriptor instanceof ConstructorDescriptor) {
245                accessor = new AccessorForFunctionDescriptor((FunctionDescriptor) descriptor, contextDescriptor, accessorIndex);
246            }
247            else if (descriptor instanceof PropertyDescriptor) {
248                if (isForBackingFieldInOuterClass) {
249                    accessor = new AccessorForPropertyBackingFieldInOuterClass((PropertyDescriptor) descriptor, contextDescriptor,
250                                                                               accessorIndex, delegateType);
251                } else {
252                    accessor = new AccessorForPropertyDescriptor((PropertyDescriptor) descriptor, contextDescriptor, accessorIndex);
253                }
254            }
255            else {
256                throw new UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor);
257            }
258            accessors.put(descriptor, accessor);
259            return accessor;
260        }
261    
262        @Nullable
263        protected StackValue.Field computeOuterExpression() {
264            return null;
265        }
266    
267        public StackValue lookupInContext(DeclarationDescriptor d, @Nullable StackValue result, GenerationState state, boolean ignoreNoOuter) {
268            StackValue myOuter = null;
269            if (closure != null) {
270                EnclosedValueDescriptor answer = closure.getCaptureVariables().get(d);
271                if (answer != null) {
272                    return StackValue.changeReceiverForFieldAndSharedVar(answer.getInnerValue(), result);
273                }
274    
275                for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
276                    if (aCase.isCase(d)) {
277                        Type classType = state.getTypeMapper().mapType(getThisDescriptor());
278                        StackValue.StackValueWithSimpleReceiver innerValue = aCase.innerValue(d, enclosingLocalLookup, state, closure, classType);
279                        if (innerValue == null) {
280                            break;
281                        }
282                        else {
283                            return StackValue.changeReceiverForFieldAndSharedVar(innerValue, result);
284                        }
285                    }
286                }
287    
288                myOuter = getOuterExpression(result, ignoreNoOuter, false);
289                result = myOuter;
290            }
291    
292            StackValue resultValue;
293            if (myOuter != null && getEnclosingClass() == d) {
294                resultValue = result;
295            } else {
296                resultValue = parentContext != null ? parentContext.lookupInContext(d, result, state, ignoreNoOuter) : null;
297            }
298    
299            if (myOuter != null && resultValue != null && !isStaticField(resultValue)) {
300                closure.setCaptureThis();
301            }
302            return resultValue;
303        }
304    
305        @NotNull
306        public Map<DeclarationDescriptor, DeclarationDescriptor> getAccessors() {
307            return accessors == null ? Collections.<DeclarationDescriptor, DeclarationDescriptor>emptyMap() : accessors;
308        }
309    
310        @NotNull
311        public PropertyDescriptor accessiblePropertyDescriptor(PropertyDescriptor propertyDescriptor) {
312            return (PropertyDescriptor) accessibleDescriptorIfNeeded(propertyDescriptor, true);
313        }
314    
315        @NotNull
316        public FunctionDescriptor accessibleFunctionDescriptor(FunctionDescriptor fd) {
317            return (FunctionDescriptor) accessibleDescriptorIfNeeded(fd, true);
318        }
319    
320        public void recordSyntheticAccessorIfNeeded(@NotNull FunctionDescriptor fd, @NotNull BindingContext bindingContext) {
321            if (fd instanceof ConstructorDescriptor || needSyntheticAccessorInBindingTrace(fd, bindingContext)) {
322                accessibleDescriptorIfNeeded(fd, false);
323            }
324        }
325    
326        public void recordSyntheticAccessorIfNeeded(@NotNull PropertyDescriptor propertyDescriptor, @NotNull BindingContext typeMapper) {
327            if (needSyntheticAccessorInBindingTrace(propertyDescriptor, typeMapper)) {
328                accessibleDescriptorIfNeeded(propertyDescriptor, false);
329            }
330        }
331    
332        private static boolean needSyntheticAccessorInBindingTrace(
333                @NotNull CallableMemberDescriptor descriptor,
334                @NotNull BindingContext bindingContext
335        ) {
336            return Boolean.TRUE.equals(bindingContext.get(BindingContext.NEED_SYNTHETIC_ACCESSOR, descriptor));
337        }
338    
339        private static int getAccessFlags(@NotNull CallableMemberDescriptor descriptor) {
340            int flag = getVisibilityAccessFlag(descriptor);
341            if (descriptor instanceof PropertyDescriptor) {
342                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
343    
344                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
345                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
346    
347                flag |= (getter == null ? 0 : getVisibilityAccessFlag(getter)) |
348                    (setter == null ? 0 : getVisibilityAccessFlag(setter));
349            }
350            return flag;
351        }
352    
353        @NotNull
354        private MemberDescriptor accessibleDescriptorIfNeeded(CallableMemberDescriptor descriptor, boolean fromOutsideContext) {
355            CallableMemberDescriptor unwrappedDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor);
356            int flag = getAccessFlags(unwrappedDescriptor);
357            if ((flag & ACC_PRIVATE) == 0 && (flag & ACC_PROTECTED) == 0) {
358                return descriptor;
359            }
360    
361            CodegenContext descriptorContext = null;
362            if (!fromOutsideContext || getClassOrPackageParentContext().getContextDescriptor() != descriptor.getContainingDeclaration()) {
363                DeclarationDescriptor enclosed = descriptor.getContainingDeclaration();
364                boolean isCompanionObjectMember = DescriptorUtils.isCompanionObject(enclosed);
365                //go upper
366                if (hasThisDescriptor() && (enclosed != getThisDescriptor() || !fromOutsideContext)) {
367                    CodegenContext currentContext = this;
368                    while (currentContext != null) {
369                        if (currentContext.getContextDescriptor() == enclosed) {
370                            descriptorContext = currentContext;
371                            break;
372                        }
373    
374                        //accessors for private members in companion object for call from class
375                        if (isCompanionObjectMember && currentContext instanceof ClassContext) {
376                            ClassContext classContext = (ClassContext) currentContext;
377                            CodegenContext companionObjectContext = classContext.getCompanionObjectContext();
378                            if (companionObjectContext != null && companionObjectContext.getContextDescriptor() == enclosed) {
379                                descriptorContext = companionObjectContext;
380                                break;
381                            }
382                        }
383    
384                        currentContext = currentContext.getParentContext();
385                    }
386                }
387            }
388    
389            if (descriptorContext == null) {
390                return descriptor;
391            }
392    
393            if ((flag & ACC_PROTECTED) != 0) {
394                PackageFragmentDescriptor unwrappedDescriptorPackage =
395                        DescriptorUtils.getParentOfType(unwrappedDescriptor, PackageFragmentDescriptor.class, false);
396                PackageFragmentDescriptor contextDescriptorPackage =
397                        DescriptorUtils.getParentOfType(descriptorContext.getContextDescriptor(), PackageFragmentDescriptor.class, false);
398    
399                boolean inSamePackage = contextDescriptorPackage != null && unwrappedDescriptorPackage != null &&
400                                        unwrappedDescriptorPackage.getFqName().equals(contextDescriptorPackage.getFqName());
401                if (inSamePackage) {
402                    return descriptor;
403                }
404            }
405    
406            return (MemberDescriptor) descriptorContext.getAccessor(descriptor);
407        }
408    
409        private void addChild(@NotNull CodegenContext child) {
410            if (shouldAddChild(child)) {
411                if (childContexts == null) {
412                    childContexts = new HashMap<DeclarationDescriptor, CodegenContext>();
413                }
414                DeclarationDescriptor childContextDescriptor = child.getContextDescriptor();
415                childContexts.put(childContextDescriptor, child);
416            }
417        }
418    
419        protected boolean shouldAddChild(@NotNull CodegenContext child) {
420            return DescriptorUtils.isCompanionObject(child.contextDescriptor);
421        }
422    
423        @Nullable
424        public CodegenContext findChildContext(@NotNull DeclarationDescriptor child) {
425            return childContexts == null ? null : childContexts.get(child);
426        }
427    
428        private static boolean isStaticField(@NotNull StackValue value) {
429            return value instanceof StackValue.Field && ((StackValue.Field) value).isStaticPut;
430        }
431    }