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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.asm4.Type;
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.descriptors.annotations.AnnotationDescriptor;
029    import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl;
030    import org.jetbrains.jet.lang.resolve.BindingContext;
031    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
032    
033    import java.util.Collections;
034    import java.util.HashMap;
035    import java.util.Map;
036    
037    import static org.jetbrains.asm4.Opcodes.ACC_PRIVATE;
038    import static org.jetbrains.jet.codegen.AsmUtil.CAPTURED_THIS_FIELD;
039    import static org.jetbrains.jet.codegen.AsmUtil.getVisibilityAccessFlag;
040    import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
041    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
042    
043    public abstract class CodegenContext<T extends DeclarationDescriptor> {
044    
045        public static final CodegenContext STATIC = new RootContext();
046    
047        @NotNull
048        private final T contextDescriptor;
049    
050        @NotNull
051        private final OwnerKind contextKind;
052    
053        @Nullable
054        private final CodegenContext parentContext;
055        private final ClassDescriptor thisDescriptor;
056    
057        public final MutableClosure closure;
058    
059        private HashMap<DeclarationDescriptor, DeclarationDescriptor> accessors;
060    
061        private Map<DeclarationDescriptor, CodegenContext> childContexts;
062    
063        protected StackValue outerExpression;
064    
065        private final LocalLookup enclosingLocalLookup;
066    
067        public CodegenContext(
068                @NotNull T contextDescriptor,
069                @NotNull OwnerKind contextKind,
070                @Nullable CodegenContext parentContext,
071                @Nullable MutableClosure closure,
072                @Nullable ClassDescriptor thisDescriptor,
073                @Nullable LocalLookup expressionCodegen
074        ) {
075            this.contextDescriptor = contextDescriptor;
076            this.contextKind = contextKind;
077            this.parentContext = parentContext;
078            this.closure = closure;
079            this.thisDescriptor = thisDescriptor;
080            this.enclosingLocalLookup = expressionCodegen;
081    
082            if (parentContext != null) {
083                parentContext.addChild(this);
084            }
085        }
086    
087        @NotNull
088        public final ClassDescriptor getThisDescriptor() {
089            if (thisDescriptor == null) {
090                throw new UnsupportedOperationException();
091            }
092            return thisDescriptor;
093        }
094    
095        public final boolean hasThisDescriptor() {
096            return thisDescriptor != null;
097        }
098    
099        public DeclarationDescriptor getClassOrNamespaceDescriptor() {
100            CodegenContext c = this;
101            while (true) {
102                assert c != null;
103                DeclarationDescriptor contextDescriptor = c.getContextDescriptor();
104                if (!(contextDescriptor instanceof ClassDescriptor) && !(contextDescriptor instanceof NamespaceDescriptor)) {
105                    c = c.getParentContext();
106                }
107                else {
108                    return contextDescriptor;
109                }
110            }
111        }
112    
113        /**
114         * This method returns not null only if context descriptor corresponds to method or function which has receiver
115         */
116        @Nullable
117        public final CallableDescriptor getCallableDescriptorWithReceiver() {
118            if (contextDescriptor instanceof CallableDescriptor) {
119                CallableDescriptor callableDescriptor = (CallableDescriptor) getContextDescriptor();
120                return callableDescriptor.getReceiverParameter() != null ? callableDescriptor : null;
121            }
122            return null;
123        }
124    
125        public StackValue getOuterExpression(@Nullable StackValue prefix, boolean ignoreNoOuter) {
126            if (outerExpression == null) {
127                if (ignoreNoOuter) {
128                    return null;
129                }
130                else {
131                    throw new UnsupportedOperationException();
132                }
133            }
134    
135            closure.setCaptureThis();
136            return prefix != null ? StackValue.composed(prefix, outerExpression) : outerExpression;
137        }
138    
139        @NotNull
140        public T getContextDescriptor() {
141            return contextDescriptor;
142        }
143    
144        @NotNull
145        public OwnerKind getContextKind() {
146            return contextKind;
147        }
148    
149        @NotNull
150        public FieldOwnerContext intoNamespace(@NotNull NamespaceDescriptor descriptor) {
151            return new NamespaceContext(descriptor, this, OwnerKind.NAMESPACE);
152        }
153    
154        @NotNull
155        public FieldOwnerContext intoNamespacePart(String delegateTo, NamespaceDescriptor descriptor) {
156            return new NamespaceContext(descriptor, this, new OwnerKind.StaticDelegateKind(delegateTo));
157        }
158    
159        @NotNull
160        public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind, GenerationState state) {
161            return new ClassContext(state.getTypeMapper(), descriptor, kind, this, null);
162        }
163    
164        @NotNull
165        public ClassContext intoAnonymousClass(
166                @NotNull ClassDescriptor descriptor,
167                @NotNull ExpressionCodegen expressionCodegen
168        ) {
169            JetTypeMapper typeMapper = expressionCodegen.getState().getTypeMapper();
170            return new AnonymousClassContext(typeMapper, descriptor, OwnerKind.IMPLEMENTATION, this,
171                                             expressionCodegen);
172        }
173    
174        @NotNull
175        public MethodContext intoFunction(FunctionDescriptor descriptor) {
176            return new MethodContext(descriptor, getContextKind(), this);
177        }
178    
179        @NotNull
180        public ConstructorContext intoConstructor(ConstructorDescriptor descriptor) {
181            if (descriptor == null) {
182                descriptor = new ConstructorDescriptorImpl(getThisDescriptor(), Collections.<AnnotationDescriptor>emptyList(), true)
183                        .initialize(Collections.<TypeParameterDescriptor>emptyList(), Collections.<ValueParameterDescriptor>emptyList(),
184                                    Visibilities.PUBLIC);
185            }
186            return new ConstructorContext(descriptor, getContextKind(), this);
187        }
188    
189        @NotNull
190        public CodegenContext intoScript(@NotNull ScriptDescriptor script, @NotNull ClassDescriptor classDescriptor) {
191            return new ScriptContext(script, classDescriptor, OwnerKind.IMPLEMENTATION, this, closure);
192        }
193    
194        @NotNull
195        public CodegenContext intoClosure(
196                @NotNull FunctionDescriptor funDescriptor,
197                @NotNull LocalLookup localLookup,
198                @NotNull JetTypeMapper typeMapper
199        ) {
200            ClassDescriptor classDescriptor = anonymousClassForFunction(typeMapper.getBindingContext(), funDescriptor);
201            return new ClosureContext(typeMapper, funDescriptor, classDescriptor, this, localLookup);
202        }
203    
204        public FrameMap prepareFrame(JetTypeMapper mapper) {
205            FrameMap frameMap = new FrameMap();
206    
207            if (getContextKind() != OwnerKind.NAMESPACE) {
208                frameMap.enterTemp(OBJECT_TYPE);  // 0 slot for this
209            }
210    
211            CallableDescriptor receiverDescriptor = getCallableDescriptorWithReceiver();
212            if (receiverDescriptor != null) {
213                Type type = mapper.mapType(receiverDescriptor.getReceiverParameter().getType());
214                frameMap.enterTemp(type);  // Next slot for receiver
215            }
216    
217            return frameMap;
218        }
219    
220        @Nullable
221        public CodegenContext getParentContext() {
222            return parentContext;
223        }
224    
225        public ClassDescriptor getEnclosingClass() {
226            CodegenContext cur = getParentContext();
227            while (cur != null && !(cur.getContextDescriptor() instanceof ClassDescriptor)) {
228                cur = cur.getParentContext();
229            }
230    
231            return cur == null ? null : (ClassDescriptor) cur.getContextDescriptor();
232        }
233    
234        @Nullable
235        public CodegenContext findParentContextWithDescriptor(DeclarationDescriptor descriptor) {
236            CodegenContext c = this;
237            while (c != null) {
238                if (c.getContextDescriptor() == descriptor) break;
239                c = c.getParentContext();
240            }
241            return c;
242        }
243    
244        public DeclarationDescriptor getAccessor(DeclarationDescriptor descriptor) {
245            if (accessors == null) {
246                accessors = new HashMap<DeclarationDescriptor, DeclarationDescriptor>();
247            }
248            descriptor = descriptor.getOriginal();
249            DeclarationDescriptor accessor = accessors.get(descriptor);
250            if (accessor != null) {
251                return accessor;
252            }
253    
254            if (descriptor instanceof SimpleFunctionDescriptor || descriptor instanceof ConstructorDescriptor) {
255                accessor = new AccessorForFunctionDescriptor(descriptor, contextDescriptor, accessors.size());
256            }
257            else if (descriptor instanceof PropertyDescriptor) {
258                accessor = new AccessorForPropertyDescriptor((PropertyDescriptor) descriptor, contextDescriptor, accessors.size());
259            }
260            else {
261                throw new UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor);
262            }
263            accessors.put(descriptor, accessor);
264            return accessor;
265        }
266    
267        public StackValue getReceiverExpression(JetTypeMapper typeMapper) {
268            assert getCallableDescriptorWithReceiver() != null;
269            @SuppressWarnings("ConstantConditions")
270            Type asmType = typeMapper.mapType(getCallableDescriptorWithReceiver().getReceiverParameter().getType());
271            return hasThisDescriptor() ? StackValue.local(1, asmType) : StackValue.local(0, asmType);
272        }
273    
274        public abstract boolean isStatic();
275    
276        protected void initOuterExpression(JetTypeMapper typeMapper, ClassDescriptor classDescriptor) {
277            ClassDescriptor enclosingClass = getEnclosingClass();
278            outerExpression = enclosingClass != null && canHaveOuter(typeMapper.getBindingContext(), classDescriptor)
279                              ? StackValue.field(typeMapper.mapType(enclosingClass),
280                                                 CodegenBinding.getJvmInternalName(typeMapper.getBindingTrace(), classDescriptor),
281                                                 CAPTURED_THIS_FIELD,
282                                                 false)
283                              : null;
284        }
285    
286        public StackValue lookupInContext(DeclarationDescriptor d, @Nullable StackValue result, GenerationState state, boolean ignoreNoOuter) {
287            MutableClosure top = closure;
288            if (top != null) {
289                EnclosedValueDescriptor answer = closure.getCaptureVariables().get(d);
290                if (answer != null) {
291                    StackValue innerValue = answer.getInnerValue();
292                    return result == null ? innerValue : StackValue.composed(result, innerValue);
293                }
294    
295                for (LocalLookup.LocalLookupCase aCase : LocalLookup.LocalLookupCase.values()) {
296                    if (aCase.isCase(d, state)) {
297                        StackValue innerValue = aCase.innerValue(d, enclosingLocalLookup, state, closure, state.getBindingContext().get(FQN, getThisDescriptor()));
298                        if (innerValue == null) {
299                            break;
300                        }
301                        else {
302                            return result == null ? innerValue : StackValue.composed(result, innerValue);
303                        }
304                    }
305                }
306    
307                StackValue outer = getOuterExpression(null, ignoreNoOuter);
308                result = result == null || outer == null ? outer : StackValue.composed(result, outer);
309            }
310    
311            return parentContext != null ? parentContext.lookupInContext(d, result, state, ignoreNoOuter) : null;
312        }
313    
314        @NotNull
315        public Map<DeclarationDescriptor, DeclarationDescriptor> getAccessors() {
316            return accessors == null ? Collections.<DeclarationDescriptor, DeclarationDescriptor>emptyMap() : accessors;
317        }
318    
319        @NotNull
320        public PropertyDescriptor accessiblePropertyDescriptor(PropertyDescriptor propertyDescriptor) {
321            return (PropertyDescriptor) accessibleDescriptorIfNeeded(propertyDescriptor, true);
322        }
323    
324        @NotNull
325        public FunctionDescriptor accessibleFunctionDescriptor(FunctionDescriptor fd) {
326            return (FunctionDescriptor) accessibleDescriptorIfNeeded(fd, true);
327        }
328    
329        @NotNull
330        public void recordSyntheticAccessorIfNeeded(@NotNull FunctionDescriptor fd, @NotNull JetTypeMapper typeMapper) {
331            if (fd instanceof ConstructorDescriptor || needSyntheticAccessorInBindingTrace(fd, typeMapper)) {
332                accessibleDescriptorIfNeeded(fd, false);
333            }
334        }
335    
336        @NotNull
337        public void recordSyntheticAccessorIfNeeded(PropertyDescriptor propertyDescriptor, JetTypeMapper typeMapper) {
338            if (needSyntheticAccessorInBindingTrace(propertyDescriptor, typeMapper)) {
339                accessibleDescriptorIfNeeded(propertyDescriptor, false);
340            }
341        }
342    
343        private boolean needSyntheticAccessorInBindingTrace(@NotNull CallableMemberDescriptor descriptor, @NotNull JetTypeMapper typeMapper) {
344            Boolean result = typeMapper.getBindingContext().get(BindingContext.NEED_SYNTHETIC_ACCESSOR, descriptor);
345            return result == null ? false : result.booleanValue();
346        }
347    
348        @NotNull
349        private int getAccessFlags(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            int flag = getAccessFlags(descriptor);
366            if ((flag & ACC_PRIVATE) == 0) {
367                return descriptor;
368            }
369    
370            CodegenContext descriptorContext = null;
371            if (!fromOutsideContext || getClassOrNamespaceDescriptor() != descriptor.getContainingDeclaration()) {
372                DeclarationDescriptor enclosed = descriptor.getContainingDeclaration();
373                boolean isClassObjectMember = DescriptorUtils.isClassObject(enclosed);
374                //go upper
375                if (hasThisDescriptor() && (enclosed != getThisDescriptor() || !fromOutsideContext)) {
376                    CodegenContext currentContext = this;
377                    while (currentContext != null) {
378                        if (currentContext.getContextDescriptor() == enclosed) {
379                            descriptorContext = currentContext;
380                            break;
381                        }
382    
383                        //accessors for private members in class object for call from class
384                        if (isClassObjectMember && currentContext instanceof ClassContext) {
385                            ClassContext classContext = (ClassContext) currentContext;
386                            CodegenContext classObject = classContext.getClassObjectContext();
387                            if (classObject != null && classObject.getContextDescriptor() == enclosed) {
388                                descriptorContext = classObject;
389                                break;
390                            }
391                        }
392    
393                        currentContext = currentContext.getParentContext();
394                    }
395                }
396            }
397    
398            return (MemberDescriptor) (descriptorContext != null ? descriptorContext.getAccessor(descriptor) : descriptor);
399        }
400    
401        private void addChild(@NotNull CodegenContext child) {
402            if (shouldAddChild(child)) {
403                if (childContexts == null) {
404                    childContexts = new HashMap<DeclarationDescriptor, CodegenContext>();
405                }
406                DeclarationDescriptor childContextDescriptor = child.getContextDescriptor();
407                childContexts.put(childContextDescriptor, child);
408            }
409        }
410    
411        protected boolean shouldAddChild(@NotNull CodegenContext child) {
412            DeclarationDescriptor childContextDescriptor = child.contextDescriptor;
413            if (childContextDescriptor instanceof ClassDescriptor) {
414                ClassKind kind = ((ClassDescriptor) childContextDescriptor).getKind();
415                    return kind == ClassKind.CLASS_OBJECT;
416            }
417            return false;
418        }
419    
420        @Nullable
421        public CodegenContext findChildContext(@NotNull DeclarationDescriptor child) {
422            return childContexts == null ? null : childContexts.get(child);
423        }
424    }