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