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