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