001    /*
002     * Copyright 2010-2015 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.kotlin.codegen;
018    
019    import com.intellij.openapi.util.Pair;
020    import com.intellij.psi.PsiElement;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple;
024    import org.jetbrains.kotlin.codegen.annotation.AnnotatedWithFakeAnnotations;
025    import org.jetbrains.kotlin.codegen.context.*;
026    import org.jetbrains.kotlin.codegen.state.GenerationState;
027    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
028    import org.jetbrains.kotlin.descriptors.*;
029    import org.jetbrains.kotlin.descriptors.annotations.Annotated;
030    import org.jetbrains.kotlin.descriptors.annotations.AnnotationSplitter;
031    import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
032    import org.jetbrains.kotlin.descriptors.annotations.Annotations;
033    import org.jetbrains.kotlin.load.java.JvmAbi;
034    import org.jetbrains.kotlin.psi.*;
035    import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
036    import org.jetbrains.kotlin.resolve.BindingContext;
037    import org.jetbrains.kotlin.resolve.DescriptorFactory;
038    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
039    import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
040    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
041    import org.jetbrains.kotlin.resolve.constants.ConstantValue;
042    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
043    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
044    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor;
045    import org.jetbrains.kotlin.storage.LockBasedStorageManager;
046    import org.jetbrains.kotlin.types.ErrorUtils;
047    import org.jetbrains.kotlin.types.KotlinType;
048    import org.jetbrains.org.objectweb.asm.FieldVisitor;
049    import org.jetbrains.org.objectweb.asm.MethodVisitor;
050    import org.jetbrains.org.objectweb.asm.Opcodes;
051    import org.jetbrains.org.objectweb.asm.Type;
052    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
053    import org.jetbrains.org.objectweb.asm.commons.Method;
054    
055    import java.util.List;
056    
057    import static org.jetbrains.kotlin.codegen.AsmUtil.*;
058    import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
059    import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*;
060    import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
061    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.PROPERTY_METADATA_TYPE;
062    import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation;
063    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
064    
065    public class PropertyCodegen {
066        private final GenerationState state;
067        private final ClassBuilder v;
068        private final FunctionCodegen functionCodegen;
069        private final JetTypeMapper typeMapper;
070        private final BindingContext bindingContext;
071        private final FieldOwnerContext context;
072        private final MemberCodegen<?> memberCodegen;
073        private final OwnerKind kind;
074    
075        public PropertyCodegen(
076                @NotNull FieldOwnerContext context,
077                @NotNull ClassBuilder v,
078                @NotNull FunctionCodegen functionCodegen,
079                @NotNull MemberCodegen<?> memberCodegen
080        ) {
081            this.state = functionCodegen.state;
082            this.v = v;
083            this.functionCodegen = functionCodegen;
084            this.typeMapper = state.getTypeMapper();
085            this.bindingContext = state.getBindingContext();
086            this.context = context;
087            this.memberCodegen = memberCodegen;
088            this.kind = context.getContextKind();
089        }
090    
091        public void gen(@NotNull KtProperty property) {
092            VariableDescriptor variableDescriptor = bindingContext.get(BindingContext.VARIABLE, property);
093            assert variableDescriptor instanceof PropertyDescriptor : "Property " + property.getText() + " should have a property descriptor: " + variableDescriptor;
094    
095            PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
096            gen(property, propertyDescriptor, property.getGetter(), property.getSetter());
097        }
098    
099        public void generateInPackageFacade(@NotNull DeserializedPropertyDescriptor deserializedProperty) {
100            assert context instanceof DelegatingFacadeContext : "should be called only for generating facade: " + context;
101            gen(null, deserializedProperty, null, null);
102        }
103    
104        private void gen(
105                @Nullable KtProperty declaration,
106                @NotNull PropertyDescriptor descriptor,
107                @Nullable KtPropertyAccessor getter,
108                @Nullable KtPropertyAccessor setter
109        ) {
110            assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.DEFAULT_IMPLS
111                    : "Generating property with a wrong kind (" + kind + "): " + descriptor;
112    
113            String implClassName = CodegenContextUtil.getImplementationClassShortName(context);
114            if (implClassName != null) {
115                v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, implClassName);
116            }
117    
118            if (CodegenContextUtil.isImplClassOwner(context)) {
119                assert declaration != null : "Declaration is null for different context: " + context;
120    
121                boolean hasBackingField = hasBackingField(declaration, descriptor);
122    
123                AnnotationSplitter annotationSplitter = AnnotationSplitter.create(LockBasedStorageManager.NO_LOCKS,
124                        descriptor.getAnnotations(), AnnotationSplitter.getTargetSet(false, descriptor.isVar(), hasBackingField));
125    
126                Annotations fieldAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.FIELD);
127                Annotations propertyAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.PROPERTY);
128    
129                generateBackingField(declaration, descriptor, fieldAnnotations);
130                generateSyntheticMethodIfNeeded(descriptor, propertyAnnotations);
131            }
132    
133            if (isAccessorNeeded(declaration, descriptor, getter)) {
134                generateGetter(declaration, descriptor, getter);
135            }
136            if (isAccessorNeeded(declaration, descriptor, setter)) {
137                generateSetter(declaration, descriptor, setter);
138            }
139    
140            context.recordSyntheticAccessorIfNeeded(descriptor, bindingContext);
141        }
142    
143        /**
144         * Determines if it's necessary to generate an accessor to the property, i.e. if this property can be referenced via getter/setter
145         * for any reason
146         *
147         * @see JvmCodegenUtil#couldUseDirectAccessToProperty
148         */
149        private boolean isAccessorNeeded(
150                @Nullable KtProperty declaration,
151                @NotNull PropertyDescriptor descriptor,
152                @Nullable KtPropertyAccessor accessor
153        ) {
154            if (hasJvmFieldAnnotation(descriptor)) return false;
155    
156            boolean isDefaultAccessor = accessor == null || !accessor.hasBody();
157    
158            // Don't generate accessors for interface properties with default accessors in DefaultImpls
159            if (kind == OwnerKind.DEFAULT_IMPLS && isDefaultAccessor) return false;
160    
161            if (declaration == null) return true;
162    
163            // Delegated or extension properties can only be referenced via accessors
164            if (declaration.hasDelegate() || declaration.getReceiverTypeReference() != null) return true;
165    
166            // Companion object properties always should have accessors, because their backing fields are moved/copied to the outer class
167            if (isCompanionObject(descriptor.getContainingDeclaration())) return true;
168    
169            // Private class properties have accessors only in cases when those accessors are non-trivial
170            if (Visibilities.isPrivate(descriptor.getVisibility())) {
171                return !isDefaultAccessor;
172            }
173    
174            return true;
175        }
176    
177        public void generatePrimaryConstructorProperty(KtParameter p, PropertyDescriptor descriptor) {
178            AnnotationSplitter annotationSplitter = AnnotationSplitter.create(LockBasedStorageManager.NO_LOCKS,
179                    descriptor.getAnnotations(), AnnotationSplitter.getTargetSet(true, descriptor.isVar(), hasBackingField(p, descriptor)));
180    
181            Annotations fieldAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.FIELD);
182            Annotations propertyAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.PROPERTY);
183    
184            generateBackingField(p, descriptor, fieldAnnotations);
185            generateSyntheticMethodIfNeeded(descriptor, propertyAnnotations);
186    
187            if (!Visibilities.isPrivate(descriptor.getVisibility())) {
188                generateGetter(p, descriptor, null);
189                if (descriptor.isVar()) {
190                    generateSetter(p, descriptor, null);
191                }
192            }
193        }
194    
195        public void generateConstructorPropertyAsMethodForAnnotationClass(KtParameter p, PropertyDescriptor descriptor) {
196            JvmMethodSignature signature = typeMapper.mapAnnotationParameterSignature(descriptor);
197            String name = p.getName();
198            if (name == null) return;
199            MethodVisitor mv = v.newMethod(
200                    JvmDeclarationOriginKt.OtherOrigin(p, descriptor), ACC_PUBLIC | ACC_ABSTRACT, name,
201                    signature.getAsmMethod().getDescriptor(),
202                    signature.getGenericsSignature(),
203                    null
204            );
205    
206            KtExpression defaultValue = p.getDefaultValue();
207            if (defaultValue != null) {
208                ConstantValue<?> constant = ExpressionCodegen.getCompileTimeConstant(defaultValue, bindingContext);
209                assert state.getClassBuilderMode() != ClassBuilderMode.FULL || constant != null
210                        : "Default value for annotation parameter should be compile time value: " + defaultValue.getText();
211                if (constant != null) {
212                    AnnotationCodegen annotationCodegen = AnnotationCodegen.forAnnotationDefaultValue(mv, typeMapper);
213                    annotationCodegen.generateAnnotationDefaultValue(constant, descriptor.getType());
214                }
215            }
216    
217            mv.visitEnd();
218        }
219    
220        private boolean hasBackingField(@NotNull KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor) {
221            return !isJvmInterface(descriptor.getContainingDeclaration()) &&
222                   kind != OwnerKind.DEFAULT_IMPLS &&
223                   !Boolean.FALSE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor));
224        }
225    
226        private boolean generateBackingField(
227                @NotNull KtNamedDeclaration p,
228                @NotNull PropertyDescriptor descriptor,
229                @NotNull Annotations annotations
230        ) {
231            if (isJvmInterface(descriptor.getContainingDeclaration()) || kind == OwnerKind.DEFAULT_IMPLS) {
232                return false;
233            }
234    
235            if (p instanceof KtProperty && ((KtProperty) p).hasDelegate()) {
236                generatePropertyDelegateAccess((KtProperty) p, descriptor, annotations);
237            }
238            else if (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) {
239                generateBackingFieldAccess(p, descriptor, annotations);
240            }
241            else {
242                return false;
243            }
244            return true;
245        }
246    
247        // Annotations on properties are stored in bytecode on an empty synthetic method. This way they're still
248        // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally
249        private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor, Annotations annotations) {
250            if (annotations.getAllAnnotations().isEmpty()) return;
251    
252            ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
253            String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
254            String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V";
255    
256            if (!isInterface(context.getContextDescriptor()) || kind == OwnerKind.DEFAULT_IMPLS) {
257                int flags = ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
258                MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), flags, name, desc, null, null);
259                AnnotationCodegen.forMethod(mv, typeMapper)
260                        .genAnnotations(new AnnotatedSimple(annotations), Type.VOID_TYPE, AnnotationUseSiteTarget.PROPERTY);
261                mv.visitCode();
262                mv.visitInsn(Opcodes.RETURN);
263                mv.visitEnd();
264            }
265            else {
266                Type tImplType = typeMapper.mapDefaultImpls((ClassDescriptor) context.getContextDescriptor());
267                v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(tImplType));
268            }
269    
270            if (kind != OwnerKind.DEFAULT_IMPLS) {
271                v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, new Method(name, desc));
272            }
273        }
274    
275        private void generateBackingField(
276                KtNamedDeclaration element,
277                PropertyDescriptor propertyDescriptor,
278                boolean isDelegate,
279                KotlinType jetType,
280                Object defaultValue,
281                Annotations annotations
282        ) {
283            int modifiers = getDeprecatedAccessFlag(propertyDescriptor);
284    
285            for (AnnotationCodegen.JvmFlagAnnotation flagAnnotation : AnnotationCodegen.FIELD_FLAGS) {
286                if (flagAnnotation.hasAnnotation(propertyDescriptor.getOriginal())) {
287                    modifiers |= flagAnnotation.getJvmFlag();
288                }
289            }
290    
291            if (kind == OwnerKind.PACKAGE) {
292                modifiers |= ACC_STATIC;
293            }
294    
295            if (!propertyDescriptor.isLateInit() && (!propertyDescriptor.isVar() || isDelegate)) {
296                modifiers |= ACC_FINAL;
297            }
298    
299            if (AnnotationUtilKt.hasJvmSyntheticAnnotation(propertyDescriptor)) {
300                modifiers |= ACC_SYNTHETIC;
301            }
302    
303            Type type = typeMapper.mapType(jetType);
304    
305            ClassBuilder builder = v;
306    
307            boolean hasJvmFieldAnnotation = hasJvmFieldAnnotation(propertyDescriptor);
308    
309            FieldOwnerContext backingFieldContext = context;
310            boolean takeVisibilityFromDescriptor = propertyDescriptor.isLateInit() || propertyDescriptor.isConst();
311            if (AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor) ) {
312                modifiers |= ACC_STATIC;
313    
314                if (takeVisibilityFromDescriptor) {
315                    modifiers |= getVisibilityAccessFlag(propertyDescriptor);
316                }
317                else if (hasJvmFieldAnnotation && !isDelegate) {
318                    modifiers |= getDefaultVisibilityFlag(propertyDescriptor.getVisibility());
319                }
320                else {
321                    modifiers |= getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate);
322                }
323    
324                if (AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) {
325                    ImplementationBodyCodegen codegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
326                    builder = codegen.v;
327                    backingFieldContext = codegen.context;
328                    v.getSerializationBindings().put(STATIC_FIELD_IN_OUTER_CLASS, propertyDescriptor);
329                }
330    
331                if (isObject(propertyDescriptor.getContainingDeclaration()) &&
332                    !hasJvmFieldAnnotation &&
333                    !propertyDescriptor.isConst() &&
334                    (modifiers & ACC_PRIVATE) == 0) {
335                    modifiers |= ACC_DEPRECATED;
336                }
337            }
338            else if (takeVisibilityFromDescriptor) {
339                modifiers |= getVisibilityAccessFlag(propertyDescriptor);
340            }
341            else if (!isDelegate && hasJvmFieldAnnotation) {
342                modifiers |= getDefaultVisibilityFlag(propertyDescriptor.getVisibility());
343            }
344            else {
345                modifiers |= ACC_PRIVATE;
346            }
347    
348            if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) {
349                ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
350                parentBodyCodegen.addCompanionObjectPropertyToCopy(propertyDescriptor, defaultValue);
351            }
352    
353            String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate);
354    
355            v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name));
356    
357            FieldVisitor fv = builder.newField(JvmDeclarationOriginKt.OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(),
358                                               typeMapper.mapFieldSignature(jetType), defaultValue);
359    
360            Annotated fieldAnnotated = new AnnotatedWithFakeAnnotations(propertyDescriptor, annotations);
361            AnnotationCodegen.forField(fv, typeMapper).genAnnotations(fieldAnnotated, type, AnnotationUseSiteTarget.FIELD);
362        }
363    
364        private void generatePropertyDelegateAccess(KtProperty p, PropertyDescriptor propertyDescriptor, Annotations annotations) {
365            KtExpression delegateExpression = p.getDelegateExpression();
366            KotlinType delegateType = delegateExpression != null ? bindingContext.getType(p.getDelegateExpression()) : null;
367            if (delegateType == null) {
368                // If delegate expression is unresolved reference
369                delegateType = ErrorUtils.createErrorType("Delegate type");
370            }
371    
372            generateBackingField(p, propertyDescriptor, true, delegateType, null, annotations);
373        }
374    
375        private void generateBackingFieldAccess(KtNamedDeclaration p, PropertyDescriptor propertyDescriptor, Annotations annotations) {
376            Object value = null;
377    
378            if (shouldWriteFieldInitializer(propertyDescriptor)) {
379                ConstantValue<?> initializer = propertyDescriptor.getCompileTimeInitializer();
380                if (initializer != null) {
381                    value = initializer.getValue();
382                }
383            }
384    
385            generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value, annotations);
386        }
387    
388        private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) {
389            //final field of primitive or String type
390            if (!descriptor.isVar()) {
391                Type type = typeMapper.mapType(descriptor);
392                return AsmUtil.isPrimitive(type) || "java.lang.String".equals(type.getClassName());
393            }
394            return false;
395        }
396    
397        private void generateGetter(@Nullable KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor getter) {
398            generateAccessor(p, getter, descriptor.getGetter() != null
399                                        ? descriptor.getGetter()
400                                        : DescriptorFactory.createDefaultGetter(descriptor, Annotations.Companion.getEMPTY()));
401        }
402    
403        private void generateSetter(@Nullable KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor setter) {
404            if (!descriptor.isVar()) return;
405    
406            generateAccessor(p, setter, descriptor.getSetter() != null
407                                        ? descriptor.getSetter()
408                                        : DescriptorFactory.createDefaultSetter(descriptor, Annotations.Companion.getEMPTY()));
409        }
410    
411        private void generateAccessor(
412                @Nullable KtNamedDeclaration p,
413                @Nullable KtPropertyAccessor accessor,
414                @NotNull PropertyAccessorDescriptor accessorDescriptor
415        ) {
416            if (context instanceof MultifileClassFacadeContext && Visibilities.isPrivate(accessorDescriptor.getVisibility())) {
417                return;
418            }
419    
420            FunctionGenerationStrategy strategy;
421            if (accessor == null || !accessor.hasBody()) {
422                if (p instanceof KtProperty && ((KtProperty) p).hasDelegate()) {
423                    strategy = new DelegatedPropertyAccessorStrategy(state, accessorDescriptor, indexOfDelegatedProperty((KtProperty) p));
424                }
425                else {
426                    strategy = new DefaultPropertyAccessorStrategy(state, accessorDescriptor);
427                }
428            }
429            else {
430                strategy = new FunctionGenerationStrategy.FunctionDefault(state, accessorDescriptor, accessor);
431            }
432    
433            functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(accessor != null ? accessor : p, accessorDescriptor), accessorDescriptor, strategy);
434        }
435    
436        public static int indexOfDelegatedProperty(@NotNull KtProperty property) {
437            PsiElement parent = property.getParent();
438            KtDeclarationContainer container;
439            if (parent instanceof KtClassBody) {
440                container = ((KtClassOrObject) parent.getParent());
441            }
442            else if (parent instanceof KtFile) {
443                container = (KtFile) parent;
444            }
445            else {
446                throw new UnsupportedOperationException("Unknown delegated property container: " + parent);
447            }
448    
449            int index = 0;
450            for (KtDeclaration declaration : container.getDeclarations()) {
451                if (declaration instanceof KtProperty && ((KtProperty) declaration).hasDelegate()) {
452                    if (declaration == property) {
453                        return index;
454                    }
455                    index++;
456                }
457            }
458    
459            throw new IllegalStateException("Delegated property not found in its parent: " + PsiUtilsKt.getElementTextWithContext(property));
460        }
461    
462    
463        private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
464            public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
465                super(state, descriptor);
466            }
467    
468            @Override
469            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
470                InstructionAdapter v = codegen.v;
471                PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty();
472                StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, true, null, StackValue.LOCAL_0);
473    
474                PsiElement jetProperty = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
475                if (jetProperty instanceof KtProperty || jetProperty instanceof KtParameter) {
476                    codegen.markLineNumber((KtElement) jetProperty, false);
477                }
478    
479                if (callableDescriptor instanceof PropertyGetterDescriptor) {
480                    Type type = signature.getReturnType();
481                    property.put(type, v);
482                    v.areturn(type);
483                }
484                else if (callableDescriptor instanceof PropertySetterDescriptor) {
485                    List<ValueParameterDescriptor> valueParameters = callableDescriptor.getValueParameters();
486                    assert valueParameters.size() == 1 : "Property setter should have only one value parameter but has " + callableDescriptor;
487                    int parameterIndex = codegen.lookupLocalIndex(valueParameters.get(0));
488                    assert parameterIndex >= 0 : "Local index for setter parameter should be positive or zero: " + callableDescriptor;
489                    Type type = codegen.typeMapper.mapType(propertyDescriptor);
490                    property.store(StackValue.local(parameterIndex, type), codegen.v);
491                    v.visitInsn(RETURN);
492                }
493                else {
494                    throw new IllegalStateException("Unknown property accessor: " + callableDescriptor);
495                }
496            }
497        }
498    
499        public static StackValue invokeDelegatedPropertyConventionMethod(
500                @NotNull PropertyDescriptor propertyDescriptor,
501                @NotNull ExpressionCodegen codegen,
502                @NotNull JetTypeMapper typeMapper,
503                @NotNull ResolvedCall<FunctionDescriptor> resolvedCall,
504                final int indexInPropertyMetadataArray,
505                int propertyMetadataArgumentIndex
506        ) {
507            CodegenContext<? extends ClassOrPackageFragmentDescriptor> ownerContext = codegen.getContext().getClassOrPackageParentContext();
508            final Type owner;
509            if (ownerContext instanceof ClassContext) {
510                owner = typeMapper.mapClass(((ClassContext) ownerContext).getContextDescriptor());
511            }
512            else if (ownerContext instanceof PackageContext) {
513                owner = ((PackageContext) ownerContext).getPackagePartType();
514            }
515            else if (ownerContext instanceof MultifileClassContextBase) {
516                owner = ((MultifileClassContextBase) ownerContext).getFilePartType();
517            }
518            else {
519                throw new UnsupportedOperationException("Unknown context: " + ownerContext);
520            }
521    
522            codegen.tempVariables.put(
523                    resolvedCall.getCall().getValueArguments().get(propertyMetadataArgumentIndex).asElement(),
524                    new StackValue(PROPERTY_METADATA_TYPE) {
525                        @Override
526                        public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
527                            Field array = StackValue
528                                    .field(Type.getType("[" + PROPERTY_METADATA_TYPE), owner, JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME, true,
529                                           StackValue.none());
530                            StackValue.arrayElement(PROPERTY_METADATA_TYPE, array, StackValue.constant(indexInPropertyMetadataArray, Type.INT_TYPE)).put(type, v);
531                        }
532                    }
533            );
534    
535            StackValue delegatedProperty = codegen.intermediateValueForProperty(propertyDescriptor, true, null, StackValue.LOCAL_0);
536            return codegen.invokeFunction(resolvedCall, delegatedProperty);
537        }
538    
539        private static class DelegatedPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
540            private final int index;
541    
542            public DelegatedPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor, int index) {
543                super(state, descriptor);
544                this.index = index;
545            }
546    
547            @Override
548            public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
549                InstructionAdapter v = codegen.v;
550    
551                BindingContext bindingContext = state.getBindingContext();
552                ResolvedCall<FunctionDescriptor> resolvedCall =
553                        bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor);
554                assert resolvedCall != null : "Resolve call should be recorded for delegate call " + signature.toString();
555    
556                StackValue lastValue = invokeDelegatedPropertyConventionMethod(callableDescriptor.getCorrespondingProperty(),
557                                                                               codegen, state.getTypeMapper(), resolvedCall, index, 1);
558                Type asmType = signature.getReturnType();
559                lastValue.put(asmType, v);
560                v.areturn(asmType);
561            }
562        }
563    
564        public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor delegateTo, @NotNull StackValue field) {
565            ClassDescriptor toClass = (ClassDescriptor) delegateTo.getContainingDeclaration();
566    
567            PropertyGetterDescriptor getter = delegate.getGetter();
568            if (getter != null) {
569                //noinspection ConstantConditions
570                functionCodegen.genDelegate(getter, delegateTo.getGetter().getOriginal(), toClass, field);
571            }
572    
573            PropertySetterDescriptor setter = delegate.getSetter();
574            if (setter != null) {
575                //noinspection ConstantConditions
576                functionCodegen.genDelegate(setter, delegateTo.getSetter().getOriginal(), toClass, field);
577            }
578        }
579    }