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
017package org.jetbrains.jet.codegen;
018
019import com.intellij.openapi.util.text.StringUtil;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.asm4.FieldVisitor;
023import org.jetbrains.asm4.MethodVisitor;
024import org.jetbrains.asm4.Type;
025import org.jetbrains.asm4.commons.InstructionAdapter;
026import org.jetbrains.jet.codegen.context.CodegenContext;
027import org.jetbrains.jet.codegen.context.FieldOwnerContext;
028import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
029import org.jetbrains.jet.codegen.signature.JvmPropertyAccessorSignature;
030import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter;
031import org.jetbrains.jet.codegen.state.GenerationState;
032import org.jetbrains.jet.codegen.state.GenerationStateAware;
033import org.jetbrains.jet.codegen.state.JetTypeMapper;
034import org.jetbrains.jet.lang.descriptors.*;
035import org.jetbrains.jet.lang.psi.*;
036import org.jetbrains.jet.lang.resolve.BindingContext;
037import org.jetbrains.jet.lang.resolve.DescriptorResolver;
038import org.jetbrains.jet.lang.resolve.DescriptorUtils;
039import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
040import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
041import org.jetbrains.jet.lang.resolve.java.JvmAbi;
042import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames;
043import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils;
044import org.jetbrains.jet.lang.resolve.name.Name;
045import org.jetbrains.jet.lang.types.ErrorUtils;
046import org.jetbrains.jet.lang.types.JetType;
047import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
048
049import static org.jetbrains.asm4.Opcodes.*;
050import static org.jetbrains.jet.codegen.AsmUtil.getDeprecatedAccessFlag;
051import static org.jetbrains.jet.codegen.AsmUtil.getVisibilityForSpecialPropertyBackingField;
052import static org.jetbrains.jet.codegen.CodegenUtil.*;
053import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
054
055public 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}