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