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