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