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