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