001    /*
002     * Copyright 2010-2014 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 com.intellij.psi.PsiElement;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.codegen.context.*;
025    import org.jetbrains.jet.codegen.state.GenerationState;
026    import org.jetbrains.jet.codegen.state.JetTypeMapper;
027    import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPropertyDescriptor;
028    import org.jetbrains.jet.lang.descriptors.*;
029    import org.jetbrains.jet.lang.psi.*;
030    import org.jetbrains.jet.lang.resolve.BindingContext;
031    import org.jetbrains.jet.lang.resolve.DescriptorFactory;
032    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
033    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
034    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
035    import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
036    import org.jetbrains.jet.lang.resolve.name.Name;
037    import org.jetbrains.jet.lang.types.ErrorUtils;
038    import org.jetbrains.jet.lang.types.JetType;
039    import org.jetbrains.org.objectweb.asm.FieldVisitor;
040    import org.jetbrains.org.objectweb.asm.MethodVisitor;
041    import org.jetbrains.org.objectweb.asm.Opcodes;
042    import org.jetbrains.org.objectweb.asm.Type;
043    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
044    import org.jetbrains.org.objectweb.asm.commons.Method;
045    
046    import static org.jetbrains.jet.codegen.AsmUtil.*;
047    import static org.jetbrains.jet.codegen.JvmCodegenUtil.getParentBodyCodegen;
048    import static org.jetbrains.jet.codegen.JvmCodegenUtil.isInterface;
049    import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
050    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
051    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait;
052    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
053    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.PROPERTY_METADATA_TYPE;
054    import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
055    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
056    
057    public class PropertyCodegen {
058        private final GenerationState state;
059        private final ClassBuilder v;
060        private final FunctionCodegen functionCodegen;
061        private final JetTypeMapper typeMapper;
062        private final BindingContext bindingContext;
063        private final FieldOwnerContext context;
064        private final MemberCodegen<?> classBodyCodegen;
065        private final OwnerKind kind;
066    
067        public PropertyCodegen(
068                @NotNull FieldOwnerContext context,
069                @NotNull ClassBuilder v,
070                @NotNull FunctionCodegen functionCodegen,
071                @Nullable MemberCodegen<?> classBodyCodegen
072        ) {
073            this.state = functionCodegen.state;
074            this.v = v;
075            this.functionCodegen = functionCodegen;
076            this.typeMapper = state.getTypeMapper();
077            this.bindingContext = state.getBindingContext();
078            this.context = context;
079            this.classBodyCodegen = classBodyCodegen;
080            this.kind = context.getContextKind();
081        }
082    
083        public void gen(@NotNull JetProperty property) {
084            VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, property);
085            assert variableDescriptor instanceof PropertyDescriptor : "Property " + property.getText() + " should have a property descriptor: " + variableDescriptor;
086    
087            PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
088            gen(property, propertyDescriptor, property.getGetter(), property.getSetter());
089        }
090    
091        public void generateInPackageFacade(@NotNull DeserializedPropertyDescriptor deserializedProperty) {
092            assert context instanceof PackageFacadeContext : "should be called only for generating package facade: " + context;
093            gen(null, deserializedProperty, null, null);
094        }
095    
096        private void gen(
097                @Nullable JetProperty declaration,
098                @NotNull PropertyDescriptor descriptor,
099                @Nullable JetPropertyAccessor getter,
100                @Nullable JetPropertyAccessor setter
101        ) {
102            assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.TRAIT_IMPL
103                    : "Generating property with a wrong kind (" + kind + "): " + descriptor;
104    
105            if (context instanceof PackageFacadeContext) {
106                Type ownerType = ((PackageFacadeContext) context).getDelegateToClassType();
107                v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(ownerType));
108            }
109            else {
110                assert declaration != null : "Declaration is null for different context: " + context;
111                if (!generateBackingField(declaration, descriptor)) {
112                    generateSyntheticMethodIfNeeded(descriptor);
113                }
114            }
115    
116            if (isAccessorNeeded(declaration, descriptor, getter)) {
117                generateGetter(declaration, descriptor, getter);
118            }
119            if (isAccessorNeeded(declaration, descriptor, setter)) {
120                generateSetter(declaration, descriptor, setter);
121            }
122    
123            context.recordSyntheticAccessorIfNeeded(descriptor, bindingContext);
124        }
125    
126        /**
127         * Determines if it's necessary to generate an accessor to the property, i.e. if this property can be referenced via getter/setter
128         * for any reason
129         *
130         * @see JvmCodegenUtil#couldUseDirectAccessToProperty
131         */
132        private boolean isAccessorNeeded(
133                @Nullable JetProperty declaration,
134                @NotNull PropertyDescriptor descriptor,
135                @Nullable JetPropertyAccessor accessor
136        ) {
137            boolean isDefaultAccessor = accessor == null || !accessor.hasBody();
138    
139            // Don't generate accessors for trait properties with default accessors in TRAIT_IMPL
140            if (kind == OwnerKind.TRAIT_IMPL && isDefaultAccessor) return false;
141    
142            if (declaration == null) return true;
143    
144            // Delegated or extension properties can only be referenced via accessors
145            if (declaration.hasDelegate() || declaration.getReceiverTypeReference() != null) return true;
146    
147            // Class object properties always should have accessors, because their backing fields are moved/copied to the outer class
148            if (isClassObject(descriptor.getContainingDeclaration())) return true;
149    
150            // Private class properties have accessors only in cases when those accessors are non-trivial
151            if (kind == OwnerKind.IMPLEMENTATION && descriptor.getVisibility() == Visibilities.PRIVATE) {
152                return !isDefaultAccessor;
153            }
154    
155            return true;
156        }
157    
158        public void generatePrimaryConstructorProperty(JetParameter p, PropertyDescriptor descriptor) {
159            generateBackingField(p, descriptor);
160            if (descriptor.getVisibility() != Visibilities.PRIVATE) {
161                generateGetter(p, descriptor, null);
162                if (descriptor.isVar()) {
163                    generateSetter(p, descriptor, null);
164                }
165            }
166        }
167    
168        public void generateConstructorPropertyAsMethodForAnnotationClass(JetParameter p, PropertyDescriptor descriptor) {
169            Type type = typeMapper.mapType(descriptor);
170            String name = p.getName();
171            assert name != null : "Annotation parameter has no name: " + p.getText();
172            MethodVisitor mv = v.newMethod(OtherOrigin(p, descriptor), ACC_PUBLIC | ACC_ABSTRACT, name, "()" + type.getDescriptor(), null, null);
173    
174            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
175                JetExpression defaultValue = p.getDefaultValue();
176                if (defaultValue != null) {
177                    CompileTimeConstant<?> constant = ExpressionCodegen.getCompileTimeConstant(defaultValue, bindingContext);
178                    assert constant != null : "Default value for annotation parameter should be compile time value: " + defaultValue.getText();
179                    AnnotationCodegen annotationCodegen = AnnotationCodegen.forAnnotationDefaultValue(mv, typeMapper);
180                    annotationCodegen.generateAnnotationDefaultValue(constant, descriptor.getType());
181                }
182            }
183    
184            mv.visitEnd();
185        }
186    
187        private boolean generateBackingField(@NotNull JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor) {
188            if (isInterface(descriptor.getContainingDeclaration()) || kind == OwnerKind.TRAIT_IMPL) {
189                return false;
190            }
191    
192            if (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) {
193                generateBackingFieldAccess(p, descriptor);
194            }
195            else if (p instanceof JetProperty && ((JetProperty) p).hasDelegate()) {
196                generatePropertyDelegateAccess((JetProperty) p, descriptor);
197            }
198            else {
199                return false;
200            }
201            return true;
202        }
203    
204        // Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still
205        // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally
206        private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor) {
207            if (descriptor.getAnnotations().isEmpty()) return;
208    
209            ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
210            String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
211            String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V";
212    
213            if (!isTrait(context.getContextDescriptor()) || kind == OwnerKind.TRAIT_IMPL) {
214                int flags = ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
215                MethodVisitor mv = v.newMethod(OtherOrigin(descriptor), flags, name, desc, null, null);
216                AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(descriptor, Type.VOID_TYPE);
217                mv.visitCode();
218                mv.visitInsn(Opcodes.RETURN);
219                mv.visitEnd();
220            }
221            else {
222                Type tImplType = typeMapper.mapTraitImpl((ClassDescriptor) context.getContextDescriptor());
223                v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(tImplType));
224            }
225    
226            if (kind != OwnerKind.TRAIT_IMPL) {
227                v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, new Method(name, desc));
228            }
229        }
230    
231        private void generateBackingField(JetNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, JetType jetType, Object defaultValue) {
232            int modifiers = getDeprecatedAccessFlag(propertyDescriptor);
233    
234            for (AnnotationCodegen.JvmFlagAnnotation flagAnnotation : AnnotationCodegen.FIELD_FLAGS) {
235                if (flagAnnotation.hasAnnotation(propertyDescriptor.getOriginal())) {
236                    modifiers |= flagAnnotation.getJvmFlag();
237                }
238            }
239    
240            if (kind == OwnerKind.PACKAGE) {
241                modifiers |= ACC_STATIC;
242            }
243    
244            if (!propertyDescriptor.isVar() || isDelegate) {
245                modifiers |= ACC_FINAL;
246            }
247    
248            Type type = typeMapper.mapType(jetType);
249    
250            ClassBuilder builder = v;
251    
252            FieldOwnerContext backingFieldContext = context;
253            if (AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) {
254                modifiers |= ACC_STATIC | getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate);
255                ImplementationBodyCodegen codegen = getParentBodyCodegen(classBodyCodegen);
256                builder = codegen.v;
257                backingFieldContext = codegen.context;
258                v.getSerializationBindings().put(STATIC_FIELD_IN_OUTER_CLASS, propertyDescriptor);
259            } else {
260                if (kind != OwnerKind.PACKAGE || isDelegate) {
261                    modifiers |= ACC_PRIVATE;
262                }
263            }
264    
265            if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
266                ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen);
267                parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue);
268            }
269    
270            String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
271    
272            v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name));
273    
274            FieldVisitor fv = builder.newField(OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(),
275                                                    typeMapper.mapFieldSignature(jetType), defaultValue);
276            AnnotationCodegen.forField(fv, typeMapper).genAnnotations(propertyDescriptor, type);
277        }
278    
279        private void generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) {
280            JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression());
281            if (delegateType == null) {
282                // If delegate expression is unresolved reference
283                delegateType = ErrorUtils.createErrorType("Delegate type");
284            }
285    
286            generateBackingField(p, propertyDescriptor, true, delegateType, null);
287        }
288    
289        private void generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) {
290            Object value = null;
291    
292            if (shouldWriteFieldInitializer(propertyDescriptor)) {
293                CompileTimeConstant<?> initializer = propertyDescriptor.getCompileTimeInitializer();
294                if (initializer != null) {
295                    value = initializer.getValue();
296                }
297            }
298    
299            generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value);
300        }
301    
302        private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) {
303            //final field of primitive or String type
304            if (!descriptor.isVar()) {
305                Type type = typeMapper.mapType(descriptor);
306                return AsmUtil.isPrimitive(type) || "java.lang.String".equals(type.getClassName());
307            }
308            return false;
309        }
310    
311        private void generateGetter(@Nullable JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor getter) {
312            generateAccessor(p, getter, descriptor.getGetter() != null
313                                        ? descriptor.getGetter()
314                                        : DescriptorFactory.createDefaultGetter(descriptor));
315        }
316    
317        private void generateSetter(@Nullable JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor setter) {
318            if (!descriptor.isVar()) return;
319    
320            generateAccessor(p, setter, descriptor.getSetter() != null
321                                        ? descriptor.getSetter()
322                                        : DescriptorFactory.createDefaultSetter(descriptor));
323        }
324    
325        private void generateAccessor(
326                @Nullable JetNamedDeclaration p,
327                @Nullable JetPropertyAccessor accessor,
328                @NotNull PropertyAccessorDescriptor accessorDescriptor
329        ) {
330            FunctionGenerationStrategy strategy;
331            if (accessor == null || !accessor.hasBody()) {
332                if (p instanceof JetProperty && ((JetProperty) p).hasDelegate()) {
333                    strategy = new DelegatedPropertyAccessorStrategy(state, accessorDescriptor, indexOfDelegatedProperty((JetProperty) p));
334                }
335                else {
336                    strategy = new DefaultPropertyAccessorStrategy(state, accessorDescriptor);
337                }
338            }
339            else {
340                strategy = new FunctionGenerationStrategy.FunctionDefault(state, accessorDescriptor, accessor);
341            }
342    
343            JvmMethodSignature signature = typeMapper.mapSignature(accessorDescriptor, kind);
344            functionCodegen.generateMethod(OtherOrigin(accessor != null ? accessor : p, accessorDescriptor), signature, accessorDescriptor, strategy);
345        }
346    
347        public static int indexOfDelegatedProperty(@NotNull JetProperty property) {
348            PsiElement parent = property.getParent();
349            JetDeclarationContainer container;
350            if (parent instanceof JetClassBody) {
351                container = ((JetClassOrObject) parent.getParent());
352            }
353            else if (parent instanceof JetFile) {
354                container = (JetFile) parent;
355            }
356            else {
357                throw new UnsupportedOperationException("Unknown delegated property container: " + parent);
358            }
359    
360            int index = 0;
361            for (JetDeclaration declaration : container.getDeclarations()) {
362                if (declaration instanceof JetProperty && ((JetProperty) declaration).hasDelegate()) {
363                    if (declaration == property) {
364                        return index;
365                    }
366                    index++;
367                }
368            }
369    
370            throw new IllegalStateException("Delegated property not found in its parent: " + JetPsiUtil.getElementTextWithContext(property));
371        }
372    
373    
374        private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
375            public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
376                super(state, descriptor);
377            }
378    
379            @Override
380            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
381                InstructionAdapter v = codegen.v;
382                PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty();
383    
384                int paramCode = 0;
385                if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) {
386                    v.load(0, OBJECT_TYPE);
387                    paramCode = 1;
388                }
389    
390                StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, true, null);
391    
392                if (callableDescriptor instanceof PropertyGetterDescriptor) {
393                    Type type = signature.getReturnType();
394                    property.put(type, v);
395                    v.areturn(type);
396                }
397                else if (callableDescriptor instanceof PropertySetterDescriptor) {
398                    ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getExtensionReceiverParameter();
399                    if (receiverParameter != null) {
400                        paramCode += codegen.typeMapper.mapType(receiverParameter.getType()).getSize();
401                    }
402                    Type type = codegen.typeMapper.mapType(propertyDescriptor);
403                    v.load(paramCode, type);
404                    property.store(type, v);
405                    v.visitInsn(RETURN);
406                } else {
407                    throw new IllegalStateException("Unknown property accessor: " + callableDescriptor);
408                }
409            }
410        }
411    
412        public static StackValue invokeDelegatedPropertyConventionMethod(
413                @NotNull PropertyDescriptor propertyDescriptor,
414                @NotNull ExpressionCodegen codegen,
415                @NotNull JetTypeMapper typeMapper,
416                @NotNull ResolvedCall<FunctionDescriptor> resolvedCall,
417                final int indexInPropertyMetadataArray,
418                int propertyMetadataArgumentIndex
419        ) {
420            if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) {
421                codegen.v.load(0, OBJECT_TYPE);
422            }
423    
424            CodegenContext<? extends ClassOrPackageFragmentDescriptor> ownerContext = codegen.getContext().getClassOrPackageParentContext();
425            final Type owner;
426            if (ownerContext instanceof ClassContext) {
427                owner = typeMapper.mapClass(((ClassContext) ownerContext).getContextDescriptor());
428            }
429            else if (ownerContext instanceof PackageContext) {
430                owner = ((PackageContext) ownerContext).getPackagePartType();
431            }
432            else {
433                throw new UnsupportedOperationException("Unknown context: " + ownerContext);
434            }
435    
436            codegen.tempVariables.put(
437                    resolvedCall.getCall().getValueArguments().get(propertyMetadataArgumentIndex).asElement(),
438                    new StackValue(PROPERTY_METADATA_TYPE) {
439                        @Override
440                        public void put(Type type, InstructionAdapter v) {
441                            v.getstatic(owner.getInternalName(), JvmAbi.PROPERTY_METADATA_ARRAY_NAME, "[" + PROPERTY_METADATA_TYPE);
442                            v.iconst(indexInPropertyMetadataArray);
443                            StackValue.arrayElement(PROPERTY_METADATA_TYPE).put(type, v);
444                        }
445                    }
446            );
447    
448            StackValue delegatedProperty = codegen.intermediateValueForProperty(propertyDescriptor, true, null);
449            return codegen.invokeFunction(resolvedCall, delegatedProperty);
450        }
451    
452        private static class DelegatedPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
453            private final int index;
454    
455            public DelegatedPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor, int index) {
456                super(state, descriptor);
457                this.index = index;
458            }
459    
460            @Override
461            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
462                InstructionAdapter v = codegen.v;
463    
464                BindingContext bindingContext = state.getBindingContext();
465                ResolvedCall<FunctionDescriptor> resolvedCall =
466                        bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor);
467                assert resolvedCall != null : "Resolve call should be recorded for delegate call " + signature.toString();
468    
469                StackValue lastValue = invokeDelegatedPropertyConventionMethod(callableDescriptor.getCorrespondingProperty(),
470                                                                               codegen, state.getTypeMapper(), resolvedCall, index, 1);
471                Type asmType = signature.getReturnType();
472                lastValue.put(asmType, v);
473                v.areturn(asmType);
474            }
475        }
476    
477        @NotNull
478        public static String getterName(Name propertyName) {
479            return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
480        }
481    
482        @NotNull
483        public static String setterName(Name propertyName) {
484            return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
485        }
486    
487        public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor delegateTo, @NotNull StackValue field) {
488            ClassDescriptor toClass = (ClassDescriptor) delegateTo.getContainingDeclaration();
489    
490            PropertyGetterDescriptor getter = delegate.getGetter();
491            if (getter != null) {
492                //noinspection ConstantConditions
493                functionCodegen.genDelegate(getter, delegateTo.getGetter().getOriginal(), toClass, field);
494            }
495    
496            PropertySetterDescriptor setter = delegate.getSetter();
497            if (setter != null) {
498                //noinspection ConstantConditions
499                functionCodegen.genDelegate(setter, delegateTo.getSetter().getOriginal(), toClass, field);
500            }
501        }
502    }