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