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