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