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
017package org.jetbrains.jet.codegen.context;
018
019import org.jetbrains.annotations.NotNull;
020import org.jetbrains.annotations.Nullable;
021import org.jetbrains.asm4.Type;
022import org.jetbrains.jet.codegen.*;
023import org.jetbrains.jet.codegen.binding.CodegenBinding;
024import org.jetbrains.jet.codegen.binding.MutableClosure;
025import org.jetbrains.jet.codegen.state.GenerationState;
026import org.jetbrains.jet.codegen.state.JetTypeMapper;
027import org.jetbrains.jet.lang.descriptors.*;
028import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
029import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl;
030import org.jetbrains.jet.lang.resolve.BindingContext;
031import org.jetbrains.jet.lang.resolve.DescriptorUtils;
032
033import java.util.Collections;
034import java.util.HashMap;
035import java.util.Map;
036
037import static org.jetbrains.asm4.Opcodes.ACC_PRIVATE;
038import static org.jetbrains.jet.codegen.AsmUtil.CAPTURED_THIS_FIELD;
039import static org.jetbrains.jet.codegen.AsmUtil.getVisibilityAccessFlag;
040import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
041import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
042
043public 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}