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.PackageFacadeContext;
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 PackageFacadeContext) {
096                Type ownerType = ((PackageFacadeContext) 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 = ExpressionCodegen.getCompileTimeConstant(defaultValue, state.getBindingContext());
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, descriptor.getType());
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 = ExpressionCodegen.getCompileTimeConstant(initializer, bindingContext);
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            public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
315                super(state, descriptor);
316            }
317    
318            @Override
319            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
320                generateDefaultAccessor(callableDescriptor, codegen.v, codegen);
321            }
322        }
323    
324        private static void generateDefaultAccessor(
325                @NotNull PropertyAccessorDescriptor accessorDescriptor,
326                @NotNull InstructionAdapter iv,
327                @NotNull ExpressionCodegen codegen
328        ) {
329            JetTypeMapper typeMapper = codegen.typeMapper;
330            CodegenContext context = codegen.context;
331            OwnerKind kind = context.getContextKind();
332    
333            PropertyDescriptor propertyDescriptor = accessorDescriptor.getCorrespondingProperty();
334            Type type = typeMapper.mapType(propertyDescriptor);
335    
336            int paramCode = 0;
337            if (kind != OwnerKind.NAMESPACE) {
338                iv.load(0, OBJECT_TYPE);
339                paramCode = 1;
340            }
341    
342            StackValue property = codegen.intermediateValueForProperty(accessorDescriptor.getCorrespondingProperty(), true, null);
343    
344            if (accessorDescriptor instanceof PropertyGetterDescriptor) {
345                property.put(type, iv);
346                iv.areturn(type);
347            }
348            else if (accessorDescriptor instanceof  PropertySetterDescriptor) {
349                ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
350                if (receiverParameter != null) {
351                    paramCode += typeMapper.mapType(receiverParameter.getType()).getSize();
352                }
353                iv.load(paramCode, type);
354    
355                property.store(type, iv);
356                iv.visitInsn(RETURN);
357            } else {
358                assert false : "Unreachable state";
359            }
360        }
361    
362        private static class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
363            public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
364                super(state, descriptor);
365            }
366    
367            @Override
368            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
369                JetTypeMapper typeMapper = codegen.typeMapper;
370                OwnerKind kind = codegen.context.getContextKind();
371                InstructionAdapter iv = codegen.v;
372                BindingContext bindingContext = state.getBindingContext();
373    
374                ResolvedCall<FunctionDescriptor> resolvedCall =
375                        bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor);
376    
377                Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, callableDescriptor);
378                assert call != null : "Call should be recorded for delegate call " + signature.toString();
379    
380                PropertyDescriptor property = callableDescriptor.getCorrespondingProperty();
381                Type asmType = typeMapper.mapType(property);
382    
383                if (kind != OwnerKind.NAMESPACE) {
384                    iv.load(0, OBJECT_TYPE);
385                }
386    
387                StackValue delegatedProperty = codegen.intermediateValueForProperty(property, true, null);
388                StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall);
389    
390                if (lastValue.type != Type.VOID_TYPE) {
391                    lastValue.put(asmType, iv);
392                    iv.areturn(asmType);
393                }
394                else {
395                    iv.areturn(Type.VOID_TYPE);
396                }
397            }
398        }
399    
400        public static String getterName(Name propertyName) {
401            return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
402        }
403    
404        public static String setterName(Name propertyName) {
405            return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
406        }
407    
408        public void genDelegate(PropertyDescriptor delegate, PropertyDescriptor overridden, StackValue field) {
409            ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration();
410    
411            functionCodegen.genDelegate(delegate.getGetter(), toClass, field,
412                                        typeMapper.mapGetterSignature(delegate, OwnerKind.IMPLEMENTATION),
413                                        typeMapper.mapGetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION));
414    
415            if (delegate.isVar()) {
416                functionCodegen.genDelegate(delegate.getSetter(), toClass, field,
417                                            typeMapper.mapSetterSignature(delegate, OwnerKind.IMPLEMENTATION),
418                                            typeMapper.mapSetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION));
419            }
420        }
421    }