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;
018    
019    import com.intellij.openapi.util.text.StringUtil;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.asm4.FieldVisitor;
023    import org.jetbrains.asm4.MethodVisitor;
024    import org.jetbrains.asm4.Type;
025    import org.jetbrains.asm4.commons.InstructionAdapter;
026    import org.jetbrains.jet.codegen.context.CodegenContext;
027    import org.jetbrains.jet.codegen.context.FieldOwnerContext;
028    import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
029    import org.jetbrains.jet.codegen.signature.JvmPropertyAccessorSignature;
030    import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter;
031    import org.jetbrains.jet.codegen.state.GenerationState;
032    import org.jetbrains.jet.codegen.state.GenerationStateAware;
033    import org.jetbrains.jet.codegen.state.JetTypeMapper;
034    import org.jetbrains.jet.lang.descriptors.*;
035    import org.jetbrains.jet.lang.psi.*;
036    import org.jetbrains.jet.lang.resolve.BindingContext;
037    import org.jetbrains.jet.lang.resolve.DescriptorResolver;
038    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
039    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
040    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
041    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
042    import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames;
043    import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils;
044    import org.jetbrains.jet.lang.resolve.name.Name;
045    import org.jetbrains.jet.lang.types.ErrorUtils;
046    import org.jetbrains.jet.lang.types.JetType;
047    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
048    
049    import static org.jetbrains.asm4.Opcodes.*;
050    import static org.jetbrains.jet.codegen.AsmUtil.getDeprecatedAccessFlag;
051    import static org.jetbrains.jet.codegen.AsmUtil.getVisibilityForSpecialPropertyBackingField;
052    import static org.jetbrains.jet.codegen.CodegenUtil.*;
053    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
054    
055    public class PropertyCodegen extends GenerationStateAware {
056        @NotNull
057        private final FunctionCodegen functionCodegen;
058    
059        @NotNull
060        private final ClassBuilder v;
061    
062        @NotNull
063        private final FieldOwnerContext context;
064    
065        @Nullable
066        private MemberCodegen classBodyCodegen;
067    
068        @NotNull
069        private final OwnerKind kind;
070    
071        public PropertyCodegen(
072                @NotNull FieldOwnerContext context,
073                @NotNull ClassBuilder v,
074                @NotNull FunctionCodegen functionCodegen,
075                @Nullable MemberCodegen classBodyCodegen
076        ) {
077            super(functionCodegen.getState());
078            this.v = v;
079            this.functionCodegen = functionCodegen;
080            this.context = context;
081            this.classBodyCodegen = classBodyCodegen;
082            this.kind = context.getContextKind();
083        }
084    
085        public void gen(JetProperty p) {
086            PropertyDescriptor propertyDescriptor = DescriptorUtils.getPropertyDescriptor(p, bindingContext);
087            assert kind instanceof OwnerKind.StaticDelegateKind || kind == OwnerKind.NAMESPACE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.TRAIT_IMPL
088                    : "Generating property with a wrong kind (" + kind + "): " + propertyDescriptor;
089    
090            if (kind != OwnerKind.TRAIT_IMPL && !(kind instanceof OwnerKind.StaticDelegateKind)) {
091                generateBackingField(p, propertyDescriptor);
092            }
093            generateGetter(p, propertyDescriptor, p.getGetter());
094            generateSetter(p, propertyDescriptor, p.getSetter());
095    
096            context.recordSyntheticAccessorIfNeeded(propertyDescriptor, typeMapper);
097        }
098    
099        public void generatePrimaryConstructorProperty(JetParameter p, PropertyDescriptor descriptor) {
100            generateBackingField(p, descriptor);
101            generateGetter(p, descriptor, null);
102            if (descriptor.isVar()) {
103                generateSetter(p, descriptor, null);
104            }
105        }
106    
107        private void generateBackingField(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
108            //noinspection ConstantConditions
109            boolean hasBackingField = bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
110            boolean isDelegated = p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null;
111            if (hasBackingField || isDelegated) {
112                DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
113                if (isInterface(containingDeclaration)) {
114                    return;
115                }
116    
117                FieldVisitor fieldVisitor = hasBackingField
118                               ? generateBackingFieldAccess(p, propertyDescriptor)
119                               : generatePropertyDelegateAccess((JetProperty) p, propertyDescriptor);
120    
121                AnnotationCodegen.forField(fieldVisitor, typeMapper).genAnnotations(propertyDescriptor);
122            }
123    
124        }
125    
126        private FieldVisitor generateBackingField(JetNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, JetType jetType, Object defaultValue) {
127            int modifiers = getDeprecatedAccessFlag(propertyDescriptor);
128    
129            if (KotlinBuiltIns.getInstance().isVolatile(propertyDescriptor)) {
130                modifiers |= ACC_VOLATILE;
131            }
132    
133            if (kind == OwnerKind.NAMESPACE) {
134                modifiers |= ACC_STATIC;
135            }
136    
137            if (!propertyDescriptor.isVar() || isDelegate) {
138                modifiers |= ACC_FINAL;
139            }
140    
141            Type type = typeMapper.mapType(jetType);
142    
143            ClassBuilder builder = v;
144    
145            FieldOwnerContext backingFieldContext = context;
146            if (AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) {
147                modifiers |= ACC_STATIC | getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate);
148                ImplementationBodyCodegen codegen = getParentBodyCodegen(classBodyCodegen);
149                builder = codegen.v;
150                backingFieldContext = codegen.context;
151            } else {
152                if (kind != OwnerKind.NAMESPACE || isDelegate) {
153                    modifiers |= ACC_PRIVATE;
154                }
155            }
156    
157            if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
158                ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen);
159                parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue);
160            }
161    
162            String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
163    
164            return builder.newField(element, modifiers, name, type.getDescriptor(),
165                                    typeMapper.mapFieldSignature(jetType), defaultValue);
166        }
167    
168        private FieldVisitor generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) {
169            JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression());
170            if (delegateType == null) {
171                // If delegate expression is unresolved reference
172                delegateType = ErrorUtils.createErrorType("Delegate type");
173            }
174    
175            return generateBackingField(p, propertyDescriptor, true, delegateType, null);
176        }
177    
178        private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
179            Object value = null;
180    
181            if (ImplementationBodyCodegen.shouldWriteFieldInitializer(propertyDescriptor, typeMapper)) {
182                JetExpression initializer = p instanceof JetProperty ? ((JetProperty) p).getInitializer() : null;
183                if (initializer != null) {
184                    CompileTimeConstant<?> compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer);
185                    value = compileTimeValue != null ? compileTimeValue.getValue() : null;
186                }
187            }
188    
189            return generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value);
190        }
191    
192        private void generateGetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor getter) {
193            boolean defaultGetter = getter == null || getter.getBodyExpression() == null;
194    
195            //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter
196            //if (!defaultGetter || isExternallyAccessible(propertyDescriptor)) {
197            JvmPropertyAccessorSignature signature = typeMapper.mapGetterSignature(propertyDescriptor, kind);
198            PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
199            getterDescriptor = getterDescriptor != null ? getterDescriptor : DescriptorResolver.createDefaultGetter(propertyDescriptor);
200    
201            if (kind != OwnerKind.TRAIT_IMPL || !defaultGetter) {
202                FunctionGenerationStrategy strategy;
203                if (defaultGetter) {
204                    if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) {
205                        strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, getterDescriptor);
206                    }
207                    else {
208                        strategy = new DefaultPropertyAccessorStrategy(state, getterDescriptor);
209                    }
210                }
211                else {
212                    strategy = new FunctionGenerationStrategy.FunctionDefault(state, getterDescriptor, getter);
213                }
214                functionCodegen.generateMethod(getter != null ? getter : p,
215                                               signature,
216                                               true,
217                                               getterDescriptor,
218                                               strategy);
219            }
220            //}
221        }
222    
223        private void generateSetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor setter) {
224            boolean defaultSetter = setter == null || setter.getBodyExpression() == null;
225    
226            //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter
227            if (/*!defaultSetter || isExternallyAccessible(propertyDescriptor) &&*/ propertyDescriptor.isVar()) {
228                JvmPropertyAccessorSignature signature = typeMapper.mapSetterSignature(propertyDescriptor, kind);
229                PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
230                setterDescriptor = setterDescriptor != null ? setterDescriptor : DescriptorResolver.createDefaultSetter(propertyDescriptor);
231    
232                if (kind != OwnerKind.TRAIT_IMPL || !defaultSetter) {
233                    FunctionGenerationStrategy strategy;
234                    if (defaultSetter) {
235                        if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) {
236                            strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, setterDescriptor);
237                        }
238                        else {
239                            strategy = new DefaultPropertyAccessorStrategy(state, setterDescriptor);
240                        }
241                    }
242                    else {
243                        strategy = new FunctionGenerationStrategy.FunctionDefault(state, setterDescriptor, setter);
244                    }
245                    functionCodegen.generateMethod(setter != null ? setter : p,
246                                                   signature,
247                                                   true,
248                                                   setterDescriptor,
249                                                   strategy);
250                }
251            }
252        }
253    
254    
255        private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
256    
257            public DefaultPropertyAccessorStrategy(
258                    @NotNull GenerationState state,
259                    @NotNull PropertyAccessorDescriptor callableDescriptor
260            ) {
261                super(state, callableDescriptor);
262            }
263    
264            @Override
265            public void doGenerateBody(
266                    ExpressionCodegen codegen, JvmMethodSignature signature
267            ) {
268                generateDefaultAccessor(callableDescriptor, codegen.v, codegen);
269            }
270        }
271    
272        private static void generateDefaultAccessor(
273                @NotNull PropertyAccessorDescriptor accessorDescriptor,
274                @NotNull InstructionAdapter iv,
275                @NotNull ExpressionCodegen codegen
276        ) {
277            JetTypeMapper typeMapper = codegen.typeMapper;
278            CodegenContext context = codegen.context;
279            OwnerKind kind = context.getContextKind();
280    
281            PropertyDescriptor propertyDescriptor = accessorDescriptor.getCorrespondingProperty();
282            Type type = typeMapper.mapType(propertyDescriptor);
283    
284            int paramCode = 0;
285            if (kind != OwnerKind.NAMESPACE) {
286                iv.load(0, OBJECT_TYPE);
287                paramCode = 1;
288            }
289    
290            StackValue property = codegen.intermediateValueForProperty(accessorDescriptor.getCorrespondingProperty(), true, null);
291    
292            if (accessorDescriptor instanceof PropertyGetterDescriptor) {
293                property.put(type, iv);
294                iv.areturn(type);
295            }
296            else if (accessorDescriptor instanceof  PropertySetterDescriptor) {
297                ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
298                if (receiverParameter != null) {
299                    paramCode += typeMapper.mapType(receiverParameter.getType()).getSize();
300                }
301                iv.load(paramCode, type);
302    
303                property.store(type, iv);
304                iv.visitInsn(RETURN);
305            } else {
306                assert false : "Unreachable state";
307            }
308        }
309    
310        private static class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
311            public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
312                super(state, descriptor);
313            }
314    
315            @Override
316            public void doGenerateBody(
317                    @NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature
318            ) {
319                JetTypeMapper typeMapper = codegen.typeMapper;
320                OwnerKind kind = codegen.context.getContextKind();
321                InstructionAdapter iv = codegen.v;
322                BindingContext bindingContext = state.getBindingContext();
323    
324                ResolvedCall<FunctionDescriptor> resolvedCall =
325                        bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor);
326    
327                Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, callableDescriptor);
328                assert call != null : "Call should be recorded for delegate call " + signature.toString();
329    
330                PropertyDescriptor property = callableDescriptor.getCorrespondingProperty();
331                Type asmType = typeMapper.mapType(property);
332    
333                if (kind != OwnerKind.NAMESPACE) {
334                    iv.load(0, OBJECT_TYPE);
335                }
336    
337                StackValue delegatedProperty = codegen.intermediateValueForProperty(property, true, null);
338                StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall);
339    
340                if (lastValue.type != Type.VOID_TYPE) {
341                    lastValue.put(asmType, iv);
342                    iv.areturn(asmType);
343                }
344                else {
345                    iv.areturn(Type.VOID_TYPE);
346                }
347            }
348        }
349    
350        public static void generateJetPropertyAnnotation(
351                MethodVisitor mv, @NotNull JvmPropertyAccessorSignature propertyAccessorSignature,
352                @NotNull PropertyDescriptor propertyDescriptor, @NotNull Visibility visibility
353        ) {
354            JetMethodAnnotationWriter aw = JetMethodAnnotationWriter.visitAnnotation(mv);
355            Modality modality = propertyDescriptor.getModality();
356            int flags = getFlagsForVisibility(visibility) | JvmStdlibNames.FLAG_PROPERTY_BIT;
357            if (isInterface(propertyDescriptor.getContainingDeclaration()) && modality != Modality.ABSTRACT) {
358                flags |= modality == Modality.FINAL
359                          ? JvmStdlibNames.FLAG_FORCE_FINAL_BIT
360                          : JvmStdlibNames.FLAG_FORCE_OPEN_BIT;
361            }
362            aw.writeFlags(flags | DescriptorKindUtils.kindToFlags(propertyDescriptor.getKind()));
363            aw.writeTypeParameters(propertyAccessorSignature.getKotlinTypeParameter());
364            aw.writePropertyType(propertyAccessorSignature.getPropertyTypeKotlinSignature());
365            aw.visitEnd();
366        }
367    
368        public static String getterName(Name propertyName) {
369            return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
370        }
371    
372        public static String setterName(Name propertyName) {
373            return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
374        }
375    
376        public void genDelegate(PropertyDescriptor delegate, PropertyDescriptor overridden, StackValue field) {
377            ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration();
378    
379            functionCodegen.genDelegate(delegate.getGetter(), toClass, field,
380                                        typeMapper.mapGetterSignature(delegate, OwnerKind.IMPLEMENTATION),
381                                        typeMapper.mapGetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION));
382    
383            if (delegate.isVar()) {
384                functionCodegen.genDelegate(delegate.getSetter(), toClass, field,
385                                            typeMapper.mapSetterSignature(delegate, OwnerKind.IMPLEMENTATION),
386                                            typeMapper.mapSetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION));
387            }
388        }
389    }