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