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.Pair;
020    import com.intellij.openapi.util.text.StringUtil;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.asm4.FieldVisitor;
024    import org.jetbrains.asm4.MethodVisitor;
025    import org.jetbrains.asm4.Opcodes;
026    import org.jetbrains.asm4.Type;
027    import org.jetbrains.asm4.commons.InstructionAdapter;
028    import org.jetbrains.asm4.commons.Method;
029    import org.jetbrains.jet.codegen.context.FieldOwnerContext;
030    import org.jetbrains.jet.codegen.context.PackageFacadeContext;
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.lang.descriptors.*;
035    import org.jetbrains.jet.lang.psi.*;
036    import org.jetbrains.jet.lang.resolve.BindingContext;
037    import org.jetbrains.jet.lang.resolve.DescriptorFactory;
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.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.*;
048    import static org.jetbrains.jet.codegen.CodegenUtil.getParentBodyCodegen;
049    import static org.jetbrains.jet.codegen.CodegenUtil.isInterface;
050    import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
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 final 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.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.TRAIT_IMPL
090                    : "Generating property with a wrong kind (" + kind + "): " + propertyDescriptor;
091    
092    
093            if (context instanceof PackageFacadeContext) {
094                Type ownerType = ((PackageFacadeContext) context).getDelegateToClassType();
095                v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, 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, bindingContext);
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 = ExpressionCodegen.getCompileTimeConstant(defaultValue, state.getBindingContext());
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, descriptor.getType());
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.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(tImplType));
173            }
174    
175            if (kind != OwnerKind.TRAIT_IMPL) {
176                v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, 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.PACKAGE) {
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.getSerializationBindings().put(STATIC_FIELD_IN_OUTER_CLASS, propertyDescriptor);
206            } else {
207                if (kind != OwnerKind.PACKAGE || 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.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(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 = ExpressionCodegen.getCompileTimeConstant(initializer, bindingContext);
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            PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
255            getterDescriptor = getterDescriptor != null ? getterDescriptor : DescriptorFactory.createDefaultGetter(propertyDescriptor);
256            JvmMethodSignature signature = typeMapper.mapSignature(getterDescriptor, kind);
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                PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
285                setterDescriptor = setterDescriptor != null ? setterDescriptor : DescriptorFactory.createDefaultSetter(propertyDescriptor);
286                JvmMethodSignature signature = typeMapper.mapSignature(setterDescriptor, kind);
287    
288                if (kind != OwnerKind.TRAIT_IMPL || !defaultSetter) {
289                    FunctionGenerationStrategy strategy;
290                    if (defaultSetter) {
291                        if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) {
292                            strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, setterDescriptor);
293                        }
294                        else {
295                            strategy = new DefaultPropertyAccessorStrategy(state, setterDescriptor);
296                        }
297                    }
298                    else {
299                        strategy = new FunctionGenerationStrategy.FunctionDefault(state, setterDescriptor, setter);
300                    }
301                    functionCodegen.generateMethod(setter != null ? setter : p,
302                                                   signature,
303                                                   setterDescriptor,
304                                                   strategy);
305                }
306            }
307        }
308    
309    
310        private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
311            public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
312                super(state, descriptor);
313            }
314    
315            @Override
316            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
317                InstructionAdapter v = codegen.v;
318                PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty();
319    
320                int paramCode = 0;
321                if (codegen.context.getContextKind() != OwnerKind.PACKAGE) {
322                    v.load(0, OBJECT_TYPE);
323                    paramCode = 1;
324                }
325    
326                StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, true, null);
327    
328                if (callableDescriptor instanceof PropertyGetterDescriptor) {
329                    Type type = signature.getReturnType();
330                    property.put(type, v);
331                    v.areturn(type);
332                }
333                else if (callableDescriptor instanceof PropertySetterDescriptor) {
334                    ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
335                    if (receiverParameter != null) {
336                        paramCode += codegen.typeMapper.mapType(receiverParameter.getType()).getSize();
337                    }
338                    Type type = codegen.typeMapper.mapType(propertyDescriptor);
339                    v.load(paramCode, type);
340                    property.store(type, v);
341                    v.visitInsn(RETURN);
342                } else {
343                    throw new IllegalStateException("Unknown property accessor: " + callableDescriptor);
344                }
345            }
346        }
347    
348        private static class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
349            public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
350                super(state, descriptor);
351            }
352    
353            @Override
354            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
355                InstructionAdapter v = codegen.v;
356    
357                BindingContext bindingContext = state.getBindingContext();
358                ResolvedCall<FunctionDescriptor> resolvedCall =
359                        bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor);
360    
361                Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, callableDescriptor);
362                assert call != null : "Call should be recorded for delegate call " + signature.toString();
363    
364                if (codegen.context.getContextKind() != OwnerKind.PACKAGE) {
365                    v.load(0, OBJECT_TYPE);
366                }
367    
368                PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty();
369    
370                StackValue delegatedProperty = codegen.intermediateValueForProperty(propertyDescriptor, true, null);
371                StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall);
372    
373                Type asmType = signature.getReturnType();
374                lastValue.put(asmType, v);
375                v.areturn(asmType);
376            }
377        }
378    
379        public static String getterName(Name propertyName) {
380            return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
381        }
382    
383        public static String setterName(Name propertyName) {
384            return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
385        }
386    
387        public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor overridden, @NotNull StackValue field) {
388            ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration();
389    
390            PropertyGetterDescriptor getter = delegate.getGetter();
391            if (getter != null) {
392                //noinspection ConstantConditions
393                functionCodegen.genDelegate(getter, toClass, field,
394                                            typeMapper.mapSignature(getter), typeMapper.mapSignature(overridden.getGetter().getOriginal()));
395            }
396    
397            PropertySetterDescriptor setter = delegate.getSetter();
398            if (setter != null) {
399                //noinspection ConstantConditions
400                functionCodegen.genDelegate(setter, toClass, field,
401                                            typeMapper.mapSignature(setter), typeMapper.mapSignature(overridden.getSetter().getOriginal()));
402            }
403        }
404    }