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