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