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