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        public void generateConstructorPropertyAsMethodForAnnotationClass(JetParameter p, PropertyDescriptor descriptor) {
108            Type type = state.getTypeMapper().mapType(descriptor);
109            MethodVisitor visitor = v.newMethod(p, ACC_PUBLIC | ACC_ABSTRACT, p.getName(), "()" + type.getDescriptor(), null, null);
110            JetExpression defaultValue = p.getDefaultValue();
111            if (defaultValue != null) {
112                CompileTimeConstant<?> constant = state.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, defaultValue);
113                assert constant != null : "Default value for annotation parameter should be compile time value: " + defaultValue.getText();
114                AnnotationCodegen annotationCodegen = AnnotationCodegen.forAnnotationDefaultValue(visitor, typeMapper);
115                annotationCodegen.generateAnnotationDefaultValue(constant);
116            }
117        }
118    
119        private void generateBackingField(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
120            //noinspection ConstantConditions
121            boolean hasBackingField = bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
122            boolean isDelegated = p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null;
123            if (hasBackingField || isDelegated) {
124                DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
125                if (isInterface(containingDeclaration)) {
126                    return;
127                }
128    
129                FieldVisitor fieldVisitor = hasBackingField
130                               ? generateBackingFieldAccess(p, propertyDescriptor)
131                               : generatePropertyDelegateAccess((JetProperty) p, propertyDescriptor);
132    
133                AnnotationCodegen.forField(fieldVisitor, typeMapper).genAnnotations(propertyDescriptor);
134            }
135    
136        }
137    
138        private FieldVisitor generateBackingField(JetNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, JetType jetType, Object defaultValue) {
139            int modifiers = getDeprecatedAccessFlag(propertyDescriptor);
140    
141            if (KotlinBuiltIns.getInstance().isVolatile(propertyDescriptor)) {
142                modifiers |= ACC_VOLATILE;
143            }
144    
145            if (kind == OwnerKind.NAMESPACE) {
146                modifiers |= ACC_STATIC;
147            }
148    
149            if (!propertyDescriptor.isVar() || isDelegate) {
150                modifiers |= ACC_FINAL;
151            }
152    
153            Type type = typeMapper.mapType(jetType);
154    
155            ClassBuilder builder = v;
156    
157            FieldOwnerContext backingFieldContext = context;
158            if (AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) {
159                modifiers |= ACC_STATIC | getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate);
160                ImplementationBodyCodegen codegen = getParentBodyCodegen(classBodyCodegen);
161                builder = codegen.v;
162                backingFieldContext = codegen.context;
163            } else {
164                if (kind != OwnerKind.NAMESPACE || isDelegate) {
165                    modifiers |= ACC_PRIVATE;
166                }
167            }
168    
169            if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
170                ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen);
171                parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue);
172            }
173    
174            String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
175    
176            return builder.newField(element, modifiers, name, type.getDescriptor(),
177                                    typeMapper.mapFieldSignature(jetType), defaultValue);
178        }
179    
180        private FieldVisitor generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) {
181            JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression());
182            if (delegateType == null) {
183                // If delegate expression is unresolved reference
184                delegateType = ErrorUtils.createErrorType("Delegate type");
185            }
186    
187            return generateBackingField(p, propertyDescriptor, true, delegateType, null);
188        }
189    
190        private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
191            Object value = null;
192    
193            if (ImplementationBodyCodegen.shouldWriteFieldInitializer(propertyDescriptor, typeMapper)) {
194                JetExpression initializer = p instanceof JetProperty ? ((JetProperty) p).getInitializer() : null;
195                if (initializer != null) {
196                    CompileTimeConstant<?> compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer);
197                    value = compileTimeValue != null ? compileTimeValue.getValue() : null;
198                }
199            }
200    
201            return generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value);
202        }
203    
204        private void generateGetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor getter) {
205            boolean defaultGetter = getter == null || getter.getBodyExpression() == null;
206    
207            //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter
208            //if (!defaultGetter || isExternallyAccessible(propertyDescriptor)) {
209            JvmPropertyAccessorSignature signature = typeMapper.mapGetterSignature(propertyDescriptor, kind);
210            PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
211            getterDescriptor = getterDescriptor != null ? getterDescriptor : DescriptorResolver.createDefaultGetter(propertyDescriptor);
212    
213            if (kind != OwnerKind.TRAIT_IMPL || !defaultGetter) {
214                FunctionGenerationStrategy strategy;
215                if (defaultGetter) {
216                    if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) {
217                        strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, getterDescriptor);
218                    }
219                    else {
220                        strategy = new DefaultPropertyAccessorStrategy(state, getterDescriptor);
221                    }
222                }
223                else {
224                    strategy = new FunctionGenerationStrategy.FunctionDefault(state, getterDescriptor, getter);
225                }
226                functionCodegen.generateMethod(getter != null ? getter : p,
227                                               signature,
228                                               true,
229                                               getterDescriptor,
230                                               strategy);
231            }
232            //}
233        }
234    
235        private void generateSetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor setter) {
236            boolean defaultSetter = setter == null || setter.getBodyExpression() == null;
237    
238            //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter
239            if (/*!defaultSetter || isExternallyAccessible(propertyDescriptor) &&*/ propertyDescriptor.isVar()) {
240                JvmPropertyAccessorSignature signature = typeMapper.mapSetterSignature(propertyDescriptor, kind);
241                PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
242                setterDescriptor = setterDescriptor != null ? setterDescriptor : DescriptorResolver.createDefaultSetter(propertyDescriptor);
243    
244                if (kind != OwnerKind.TRAIT_IMPL || !defaultSetter) {
245                    FunctionGenerationStrategy strategy;
246                    if (defaultSetter) {
247                        if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) {
248                            strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, setterDescriptor);
249                        }
250                        else {
251                            strategy = new DefaultPropertyAccessorStrategy(state, setterDescriptor);
252                        }
253                    }
254                    else {
255                        strategy = new FunctionGenerationStrategy.FunctionDefault(state, setterDescriptor, setter);
256                    }
257                    functionCodegen.generateMethod(setter != null ? setter : p,
258                                                   signature,
259                                                   true,
260                                                   setterDescriptor,
261                                                   strategy);
262                }
263            }
264        }
265    
266    
267        private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
268    
269            public DefaultPropertyAccessorStrategy(
270                    @NotNull GenerationState state,
271                    @NotNull PropertyAccessorDescriptor callableDescriptor
272            ) {
273                super(state, callableDescriptor);
274            }
275    
276            @Override
277            public void doGenerateBody(
278                    ExpressionCodegen codegen, JvmMethodSignature signature
279            ) {
280                generateDefaultAccessor(callableDescriptor, codegen.v, codegen);
281            }
282        }
283    
284        private static void generateDefaultAccessor(
285                @NotNull PropertyAccessorDescriptor accessorDescriptor,
286                @NotNull InstructionAdapter iv,
287                @NotNull ExpressionCodegen codegen
288        ) {
289            JetTypeMapper typeMapper = codegen.typeMapper;
290            CodegenContext context = codegen.context;
291            OwnerKind kind = context.getContextKind();
292    
293            PropertyDescriptor propertyDescriptor = accessorDescriptor.getCorrespondingProperty();
294            Type type = typeMapper.mapType(propertyDescriptor);
295    
296            int paramCode = 0;
297            if (kind != OwnerKind.NAMESPACE) {
298                iv.load(0, OBJECT_TYPE);
299                paramCode = 1;
300            }
301    
302            StackValue property = codegen.intermediateValueForProperty(accessorDescriptor.getCorrespondingProperty(), true, null);
303    
304            if (accessorDescriptor instanceof PropertyGetterDescriptor) {
305                property.put(type, iv);
306                iv.areturn(type);
307            }
308            else if (accessorDescriptor instanceof  PropertySetterDescriptor) {
309                ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
310                if (receiverParameter != null) {
311                    paramCode += typeMapper.mapType(receiverParameter.getType()).getSize();
312                }
313                iv.load(paramCode, type);
314    
315                property.store(type, iv);
316                iv.visitInsn(RETURN);
317            } else {
318                assert false : "Unreachable state";
319            }
320        }
321    
322        private static class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
323            public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
324                super(state, descriptor);
325            }
326    
327            @Override
328            public void doGenerateBody(
329                    @NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature
330            ) {
331                JetTypeMapper typeMapper = codegen.typeMapper;
332                OwnerKind kind = codegen.context.getContextKind();
333                InstructionAdapter iv = codegen.v;
334                BindingContext bindingContext = state.getBindingContext();
335    
336                ResolvedCall<FunctionDescriptor> resolvedCall =
337                        bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor);
338    
339                Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, callableDescriptor);
340                assert call != null : "Call should be recorded for delegate call " + signature.toString();
341    
342                PropertyDescriptor property = callableDescriptor.getCorrespondingProperty();
343                Type asmType = typeMapper.mapType(property);
344    
345                if (kind != OwnerKind.NAMESPACE) {
346                    iv.load(0, OBJECT_TYPE);
347                }
348    
349                StackValue delegatedProperty = codegen.intermediateValueForProperty(property, true, null);
350                StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall);
351    
352                if (lastValue.type != Type.VOID_TYPE) {
353                    lastValue.put(asmType, iv);
354                    iv.areturn(asmType);
355                }
356                else {
357                    iv.areturn(Type.VOID_TYPE);
358                }
359            }
360        }
361    
362        public static void generateJetPropertyAnnotation(
363                MethodVisitor mv, @NotNull JvmPropertyAccessorSignature propertyAccessorSignature,
364                @NotNull PropertyDescriptor propertyDescriptor, @NotNull Visibility visibility
365        ) {
366            JetMethodAnnotationWriter aw = JetMethodAnnotationWriter.visitAnnotation(mv);
367            Modality modality = propertyDescriptor.getModality();
368            int flags = getFlagsForVisibility(visibility) | JvmStdlibNames.FLAG_PROPERTY_BIT;
369            if (isInterface(propertyDescriptor.getContainingDeclaration()) && modality != Modality.ABSTRACT) {
370                flags |= modality == Modality.FINAL
371                          ? JvmStdlibNames.FLAG_FORCE_FINAL_BIT
372                          : JvmStdlibNames.FLAG_FORCE_OPEN_BIT;
373            }
374            aw.writeFlags(flags | DescriptorKindUtils.kindToFlags(propertyDescriptor.getKind()));
375            aw.writeTypeParameters(propertyAccessorSignature.getKotlinTypeParameter());
376            aw.writePropertyType(propertyAccessorSignature.getPropertyTypeKotlinSignature());
377            aw.visitEnd();
378        }
379    
380        public static String getterName(Name propertyName) {
381            return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
382        }
383    
384        public static String setterName(Name propertyName) {
385            return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
386        }
387    
388        public void genDelegate(PropertyDescriptor delegate, PropertyDescriptor overridden, StackValue field) {
389            ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration();
390    
391            functionCodegen.genDelegate(delegate.getGetter(), toClass, field,
392                                        typeMapper.mapGetterSignature(delegate, OwnerKind.IMPLEMENTATION),
393                                        typeMapper.mapGetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION));
394    
395            if (delegate.isVar()) {
396                functionCodegen.genDelegate(delegate.getSetter(), toClass, field,
397                                            typeMapper.mapSetterSignature(delegate, OwnerKind.IMPLEMENTATION),
398                                            typeMapper.mapSetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION));
399            }
400        }
401    }