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