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.CodegenBinding;
024    import org.jetbrains.jet.codegen.binding.MutableClosure;
025    import org.jetbrains.jet.codegen.state.GenerationState;
026    import org.jetbrains.jet.codegen.state.JetTypeMapper;
027    import org.jetbrains.jet.lang.descriptors.*;
028    import org.jetbrains.jet.lang.resolve.BindingContext;
029    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
030    import org.jetbrains.jet.lang.types.JetType;
031    import org.jetbrains.jet.storage.LockBasedStorageManager;
032    import org.jetbrains.jet.storage.NullableLazyValue;
033    import org.jetbrains.org.objectweb.asm.Type;
034    
035    import java.util.Collections;
036    import java.util.HashMap;
037    import java.util.List;
038    import java.util.Map;
039    
040    import static org.jetbrains.jet.codegen.AsmUtil.CAPTURED_THIS_FIELD;
041    import static org.jetbrains.jet.codegen.AsmUtil.getVisibilityAccessFlag;
042    import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
043    import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE;
044    import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PROTECTED;
045    
046    public abstract class CodegenContext<T extends DeclarationDescriptor> {
047        public static final CodegenContext STATIC = new RootContext();
048    
049        private final T contextDescriptor;
050        private final OwnerKind contextKind;
051        private final CodegenContext parentContext;
052        private final ClassDescriptor thisDescriptor;
053        public final MutableClosure closure;
054        private final LocalLookup enclosingLocalLookup;
055    
056        private Map<DeclarationDescriptor, DeclarationDescriptor> accessors;
057        private Map<DeclarationDescriptor, CodegenContext> childContexts;
058        private NullableLazyValue<StackValue> lazyOuterExpression;
059    
060        public CodegenContext(
061                @NotNull T contextDescriptor,
062                @NotNull OwnerKind contextKind,
063                @Nullable CodegenContext parentContext,
064                @Nullable MutableClosure closure,
065                @Nullable ClassDescriptor thisDescriptor,
066                @Nullable LocalLookup expressionCodegen
067        ) {
068            this.contextDescriptor = contextDescriptor;
069            this.contextKind = contextKind;
070            this.parentContext = parentContext;
071            this.closure = closure;
072            this.thisDescriptor = thisDescriptor;
073            this.enclosingLocalLookup = expressionCodegen;
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.getReceiverParameter() != 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 (lazyOuterExpression == null || lazyOuterExpression.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 prefix != null ? StackValue.composed(prefix, lazyOuterExpression.invoke()) : lazyOuterExpression.invoke();
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) {
164            return new AnonymousClassContext(codegen.getState().getTypeMapper(), descriptor, OwnerKind.IMPLEMENTATION, 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 CodegenContext intoClosure(
194                @NotNull FunctionDescriptor funDescriptor,
195                @NotNull LocalLookup localLookup,
196                @NotNull JetTypeMapper typeMapper
197        ) {
198            ClassDescriptor classDescriptor = anonymousClassForFunction(typeMapper.getBindingContext(), funDescriptor);
199            return new ClosureContext(typeMapper, funDescriptor, classDescriptor, this, localLookup);
200        }
201    
202        @Nullable
203        public CodegenContext getParentContext() {
204            return parentContext;
205        }
206    
207        public ClassDescriptor getEnclosingClass() {
208            CodegenContext cur = getParentContext();
209            while (cur != null && !(cur.getContextDescriptor() instanceof ClassDescriptor)) {
210                cur = cur.getParentContext();
211            }
212    
213            return cur == null ? null : (ClassDescriptor) cur.getContextDescriptor();
214        }
215    
216        @Nullable
217        public CodegenContext findParentContextWithDescriptor(DeclarationDescriptor descriptor) {
218            CodegenContext c = this;
219            while (c != null) {
220                if (c.getContextDescriptor() == descriptor) break;
221                c = c.getParentContext();
222            }
223            return c;
224        }
225    
226        @NotNull
227        public DeclarationDescriptor getAccessor(@NotNull DeclarationDescriptor descriptor) {
228            return getAccessor(descriptor, false, null);
229        }
230    
231        @NotNull
232        public DeclarationDescriptor getAccessor(@NotNull DeclarationDescriptor descriptor, boolean isForBackingFieldInOuterClass, @Nullable JetType delegateType) {
233            if (accessors == null) {
234                accessors = new HashMap<DeclarationDescriptor, DeclarationDescriptor>();
235            }
236            descriptor = descriptor.getOriginal();
237            DeclarationDescriptor accessor = accessors.get(descriptor);
238            if (accessor != null) {
239                assert !isForBackingFieldInOuterClass ||
240                       accessor instanceof AccessorForPropertyBackingFieldInOuterClass : "There is already exists accessor with isForBackingFieldInOuterClass = false in this context";
241                return accessor;
242            }
243    
244            int accessorIndex = accessors.size();
245            if (descriptor instanceof SimpleFunctionDescriptor || descriptor instanceof ConstructorDescriptor) {
246                accessor = new AccessorForFunctionDescriptor((FunctionDescriptor) descriptor, contextDescriptor, accessorIndex);
247            }
248            else if (descriptor instanceof PropertyDescriptor) {
249                if (isForBackingFieldInOuterClass) {
250                    accessor = new AccessorForPropertyBackingFieldInOuterClass((PropertyDescriptor) descriptor, contextDescriptor,
251                                                                               accessorIndex, delegateType);
252                } else {
253                    accessor = new AccessorForPropertyDescriptor((PropertyDescriptor) descriptor, contextDescriptor, accessorIndex);
254                }
255            }
256            else {
257                throw new UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor);
258            }
259            accessors.put(descriptor, accessor);
260            return accessor;
261        }
262    
263        public StackValue getReceiverExpression(JetTypeMapper typeMapper) {
264            assert getCallableDescriptorWithReceiver() != null;
265            @SuppressWarnings("ConstantConditions")
266            Type asmType = typeMapper.mapType(getCallableDescriptorWithReceiver().getReceiverParameter().getType());
267            return hasThisDescriptor() ? StackValue.local(1, asmType) : StackValue.local(0, asmType);
268        }
269    
270        public abstract boolean isStatic();
271    
272        protected void initOuterExpression(@NotNull final JetTypeMapper typeMapper, @NotNull final ClassDescriptor classDescriptor) {
273            lazyOuterExpression = LockBasedStorageManager.NO_LOCKS.createNullableLazyValue(new Function0<StackValue>() {
274                @Override
275                public StackValue invoke() {
276                    BindingContext bindingContext = typeMapper.getBindingContext();
277                    ClassDescriptor enclosingClass = getEnclosingClass();
278                    return enclosingClass != null && canHaveOuter(bindingContext, classDescriptor)
279                           ? StackValue.field(typeMapper.mapType(enclosingClass),
280                                              CodegenBinding.getAsmType(bindingContext, classDescriptor),
281                                              CAPTURED_THIS_FIELD,
282                                              false)
283                           : null;
284                }
285            });
286        }
287    
288        public StackValue lookupInContext(DeclarationDescriptor d, @Nullable StackValue result, GenerationState state, boolean ignoreNoOuter) {
289            StackValue myOuter = null;
290            if (closure != null) {
291                EnclosedValueDescriptor answer = closure.getCaptureVariables().get(d);
292                if (answer != null) {
293                    StackValue innerValue = answer.getInnerValue();
294                    return result == null ? innerValue : StackValue.composed(result, innerValue);
295                }
296    
297                for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
298                    if (aCase.isCase(d)) {
299                        Type classType = state.getBindingContext().get(ASM_TYPE, getThisDescriptor());
300                        StackValue innerValue = aCase.innerValue(d, enclosingLocalLookup, state, closure, classType);
301                        if (innerValue == null) {
302                            break;
303                        }
304                        else {
305                            return result == null ? innerValue : composedOrStatic(result, innerValue);
306                        }
307                    }
308                }
309    
310                myOuter = getOuterExpression(null, ignoreNoOuter, false);
311                result = result == null || myOuter == null ? myOuter : StackValue.composed(result, myOuter);
312            }
313    
314            StackValue resultValue;
315            if (myOuter != null && getEnclosingClass() == d) {
316                resultValue = result;
317            } else {
318                resultValue = parentContext != null ? parentContext.lookupInContext(d, result, state, ignoreNoOuter) : null;
319            }
320    
321            if (myOuter != null && resultValue != null && !isStaticField(resultValue)) {
322                closure.setCaptureThis();
323            }
324            return resultValue;
325        }
326    
327        @NotNull
328        public Map<DeclarationDescriptor, DeclarationDescriptor> getAccessors() {
329            return accessors == null ? Collections.<DeclarationDescriptor, DeclarationDescriptor>emptyMap() : accessors;
330        }
331    
332        @NotNull
333        public PropertyDescriptor accessiblePropertyDescriptor(PropertyDescriptor propertyDescriptor) {
334            return (PropertyDescriptor) accessibleDescriptorIfNeeded(propertyDescriptor, true);
335        }
336    
337        @NotNull
338        public FunctionDescriptor accessibleFunctionDescriptor(FunctionDescriptor fd) {
339            return (FunctionDescriptor) accessibleDescriptorIfNeeded(fd, true);
340        }
341    
342        public void recordSyntheticAccessorIfNeeded(@NotNull FunctionDescriptor fd, @NotNull BindingContext bindingContext) {
343            if (fd instanceof ConstructorDescriptor || needSyntheticAccessorInBindingTrace(fd, bindingContext)) {
344                accessibleDescriptorIfNeeded(fd, false);
345            }
346        }
347    
348        public void recordSyntheticAccessorIfNeeded(@NotNull PropertyDescriptor propertyDescriptor, @NotNull BindingContext typeMapper) {
349            if (needSyntheticAccessorInBindingTrace(propertyDescriptor, typeMapper)) {
350                accessibleDescriptorIfNeeded(propertyDescriptor, false);
351            }
352        }
353    
354        private static boolean needSyntheticAccessorInBindingTrace(
355                @NotNull CallableMemberDescriptor descriptor,
356                @NotNull BindingContext bindingContext
357        ) {
358            return Boolean.TRUE.equals(bindingContext.get(BindingContext.NEED_SYNTHETIC_ACCESSOR, descriptor));
359        }
360    
361        private static int getAccessFlags(@NotNull CallableMemberDescriptor descriptor) {
362            int flag = getVisibilityAccessFlag(descriptor);
363            if (descriptor instanceof PropertyDescriptor) {
364                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
365    
366                PropertySetterDescriptor setter = propertyDescriptor.getSetter();
367                PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
368    
369                flag |= (getter == null ? 0 : getVisibilityAccessFlag(getter)) |
370                    (setter == null ? 0 : getVisibilityAccessFlag(setter));
371            }
372            return flag;
373        }
374    
375        @NotNull
376        private MemberDescriptor accessibleDescriptorIfNeeded(CallableMemberDescriptor descriptor, boolean fromOutsideContext) {
377            CallableMemberDescriptor unwrappedDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor);
378            int flag = getAccessFlags(unwrappedDescriptor);
379            if ((flag & ACC_PRIVATE) == 0 && (flag & ACC_PROTECTED) == 0) {
380                return descriptor;
381            }
382    
383            CodegenContext descriptorContext = null;
384            if (!fromOutsideContext || getClassOrPackageParentContext().getContextDescriptor() != descriptor.getContainingDeclaration()) {
385                DeclarationDescriptor enclosed = descriptor.getContainingDeclaration();
386                boolean isClassObjectMember = DescriptorUtils.isClassObject(enclosed);
387                //go upper
388                if (hasThisDescriptor() && (enclosed != getThisDescriptor() || !fromOutsideContext)) {
389                    CodegenContext currentContext = this;
390                    while (currentContext != null) {
391                        if (currentContext.getContextDescriptor() == enclosed) {
392                            descriptorContext = currentContext;
393                            break;
394                        }
395    
396                        //accessors for private members in class object for call from class
397                        if (isClassObjectMember && currentContext instanceof ClassContext) {
398                            ClassContext classContext = (ClassContext) currentContext;
399                            CodegenContext classObject = classContext.getClassObjectContext();
400                            if (classObject != null && classObject.getContextDescriptor() == enclosed) {
401                                descriptorContext = classObject;
402                                break;
403                            }
404                        }
405    
406                        currentContext = currentContext.getParentContext();
407                    }
408                }
409            }
410    
411            if (descriptorContext == null) {
412                return descriptor;
413            }
414    
415            if ((flag & ACC_PROTECTED) != 0) {
416                PackageFragmentDescriptor unwrappedDescriptorPackage =
417                        DescriptorUtils.getParentOfType(unwrappedDescriptor, PackageFragmentDescriptor.class, false);
418                PackageFragmentDescriptor contextDescriptorPackage =
419                        DescriptorUtils.getParentOfType(descriptorContext.getContextDescriptor(), PackageFragmentDescriptor.class, false);
420    
421                boolean inSamePackage = contextDescriptorPackage != null && unwrappedDescriptorPackage != null &&
422                                        unwrappedDescriptorPackage.getFqName().equals(contextDescriptorPackage.getFqName());
423                if (inSamePackage) {
424                    return descriptor;
425                }
426            }
427    
428            return (MemberDescriptor) descriptorContext.getAccessor(descriptor);
429        }
430    
431        private void addChild(@NotNull CodegenContext child) {
432            if (shouldAddChild(child)) {
433                if (childContexts == null) {
434                    childContexts = new HashMap<DeclarationDescriptor, CodegenContext>();
435                }
436                DeclarationDescriptor childContextDescriptor = child.getContextDescriptor();
437                childContexts.put(childContextDescriptor, child);
438            }
439        }
440    
441        protected boolean shouldAddChild(@NotNull CodegenContext child) {
442            DeclarationDescriptor childContextDescriptor = child.contextDescriptor;
443            if (childContextDescriptor instanceof ClassDescriptor) {
444                ClassKind kind = ((ClassDescriptor) childContextDescriptor).getKind();
445                    return kind == ClassKind.CLASS_OBJECT;
446            }
447            return false;
448        }
449    
450        @Nullable
451        public CodegenContext findChildContext(@NotNull DeclarationDescriptor child) {
452            return childContexts == null ? null : childContexts.get(child);
453        }
454    
455        @NotNull
456        private static StackValue composedOrStatic(@NotNull StackValue prefix, @NotNull StackValue suffix) {
457            if (isStaticField(suffix)) {
458                return suffix;
459            }
460            return StackValue.composed(prefix, suffix);
461        }
462    
463        private static boolean isStaticField(@NotNull StackValue value) {
464            return value instanceof StackValue.Field && ((StackValue.Field) value).isStatic;
465        }
466    }