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