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