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.codegen.state.GenerationState; 026 import org.jetbrains.jet.codegen.state.JetTypeMapper; 027 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPropertyDescriptor; 028 import org.jetbrains.jet.lang.descriptors.*; 029 import org.jetbrains.jet.lang.psi.*; 030 import org.jetbrains.jet.lang.resolve.BindingContext; 031 import org.jetbrains.jet.lang.resolve.DescriptorFactory; 032 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 033 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; 034 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 035 import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; 036 import org.jetbrains.jet.lang.resolve.name.Name; 037 import org.jetbrains.jet.lang.types.ErrorUtils; 038 import org.jetbrains.jet.lang.types.JetType; 039 import org.jetbrains.org.objectweb.asm.FieldVisitor; 040 import org.jetbrains.org.objectweb.asm.MethodVisitor; 041 import org.jetbrains.org.objectweb.asm.Opcodes; 042 import org.jetbrains.org.objectweb.asm.Type; 043 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 044 import org.jetbrains.org.objectweb.asm.commons.Method; 045 046 import static org.jetbrains.jet.codegen.AsmUtil.*; 047 import static org.jetbrains.jet.codegen.JvmCodegenUtil.getParentBodyCodegen; 048 import static org.jetbrains.jet.codegen.JvmCodegenUtil.isInterface; 049 import static org.jetbrains.jet.codegen.JvmSerializationBindings.*; 050 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject; 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.jet.lang.resolve.java.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<?> classBodyCodegen; 065 private final OwnerKind kind; 066 067 public PropertyCodegen( 068 @NotNull FieldOwnerContext context, 069 @NotNull ClassBuilder v, 070 @NotNull FunctionCodegen functionCodegen, 071 @Nullable MemberCodegen<?> classBodyCodegen 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.classBodyCodegen = classBodyCodegen; 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 // Class object properties always should have accessors, because their backing fields are moved/copied to the outer class 148 if (isClassObject(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 && descriptor.getVisibility() == Visibilities.PRIVATE) { 152 return !isDefaultAccessor; 153 } 154 155 return true; 156 } 157 158 public void generatePrimaryConstructorProperty(JetParameter p, PropertyDescriptor descriptor) { 159 generateBackingField(p, descriptor); 160 if (descriptor.getVisibility() != Visibilities.PRIVATE) { 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 (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) { 193 generateBackingFieldAccess(p, descriptor); 194 } 195 else if (p instanceof JetProperty && ((JetProperty) p).hasDelegate()) { 196 generatePropertyDelegateAccess((JetProperty) 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.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) { 254 modifiers |= ACC_STATIC | getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate); 255 ImplementationBodyCodegen codegen = getParentBodyCodegen(classBodyCodegen); 256 builder = codegen.v; 257 backingFieldContext = codegen.context; 258 v.getSerializationBindings().put(STATIC_FIELD_IN_OUTER_CLASS, propertyDescriptor); 259 } else { 260 if (kind != OwnerKind.PACKAGE || isDelegate) { 261 modifiers |= ACC_PRIVATE; 262 } 263 } 264 265 if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) { 266 ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen); 267 parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue); 268 } 269 270 String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate); 271 272 v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name)); 273 274 FieldVisitor fv = builder.newField(OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(), 275 typeMapper.mapFieldSignature(jetType), defaultValue); 276 AnnotationCodegen.forField(fv, typeMapper).genAnnotations(propertyDescriptor, type); 277 } 278 279 private void generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) { 280 JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression()); 281 if (delegateType == null) { 282 // If delegate expression is unresolved reference 283 delegateType = ErrorUtils.createErrorType("Delegate type"); 284 } 285 286 generateBackingField(p, propertyDescriptor, true, delegateType, null); 287 } 288 289 private void generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) { 290 Object value = null; 291 292 if (shouldWriteFieldInitializer(propertyDescriptor)) { 293 CompileTimeConstant<?> initializer = propertyDescriptor.getCompileTimeInitializer(); 294 if (initializer != null) { 295 value = initializer.getValue(); 296 } 297 } 298 299 generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value); 300 } 301 302 private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) { 303 //final field of primitive or String type 304 if (!descriptor.isVar()) { 305 Type type = typeMapper.mapType(descriptor); 306 return AsmUtil.isPrimitive(type) || "java.lang.String".equals(type.getClassName()); 307 } 308 return false; 309 } 310 311 private void generateGetter(@Nullable JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor getter) { 312 generateAccessor(p, getter, descriptor.getGetter() != null 313 ? descriptor.getGetter() 314 : DescriptorFactory.createDefaultGetter(descriptor)); 315 } 316 317 private void generateSetter(@Nullable JetNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable JetPropertyAccessor setter) { 318 if (!descriptor.isVar()) return; 319 320 generateAccessor(p, setter, descriptor.getSetter() != null 321 ? descriptor.getSetter() 322 : DescriptorFactory.createDefaultSetter(descriptor)); 323 } 324 325 private void generateAccessor( 326 @Nullable JetNamedDeclaration p, 327 @Nullable JetPropertyAccessor accessor, 328 @NotNull PropertyAccessorDescriptor accessorDescriptor 329 ) { 330 FunctionGenerationStrategy strategy; 331 if (accessor == null || !accessor.hasBody()) { 332 if (p instanceof JetProperty && ((JetProperty) p).hasDelegate()) { 333 strategy = new DelegatedPropertyAccessorStrategy(state, accessorDescriptor, indexOfDelegatedProperty((JetProperty) p)); 334 } 335 else { 336 strategy = new DefaultPropertyAccessorStrategy(state, accessorDescriptor); 337 } 338 } 339 else { 340 strategy = new FunctionGenerationStrategy.FunctionDefault(state, accessorDescriptor, accessor); 341 } 342 343 JvmMethodSignature signature = typeMapper.mapSignature(accessorDescriptor, kind); 344 functionCodegen.generateMethod(OtherOrigin(accessor != null ? accessor : p, accessorDescriptor), signature, 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 384 int paramCode = 0; 385 if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) { 386 v.load(0, OBJECT_TYPE); 387 paramCode = 1; 388 } 389 390 StackValue property = codegen.intermediateValueForProperty(propertyDescriptor, true, null); 391 392 if (callableDescriptor instanceof PropertyGetterDescriptor) { 393 Type type = signature.getReturnType(); 394 property.put(type, v); 395 v.areturn(type); 396 } 397 else if (callableDescriptor instanceof PropertySetterDescriptor) { 398 ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getExtensionReceiverParameter(); 399 if (receiverParameter != null) { 400 paramCode += codegen.typeMapper.mapType(receiverParameter.getType()).getSize(); 401 } 402 Type type = codegen.typeMapper.mapType(propertyDescriptor); 403 v.load(paramCode, type); 404 property.store(type, v); 405 v.visitInsn(RETURN); 406 } else { 407 throw new IllegalStateException("Unknown property accessor: " + callableDescriptor); 408 } 409 } 410 } 411 412 public static StackValue invokeDelegatedPropertyConventionMethod( 413 @NotNull PropertyDescriptor propertyDescriptor, 414 @NotNull ExpressionCodegen codegen, 415 @NotNull JetTypeMapper typeMapper, 416 @NotNull ResolvedCall<FunctionDescriptor> resolvedCall, 417 final int indexInPropertyMetadataArray, 418 int propertyMetadataArgumentIndex 419 ) { 420 if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) { 421 codegen.v.load(0, OBJECT_TYPE); 422 } 423 424 CodegenContext<? extends ClassOrPackageFragmentDescriptor> ownerContext = codegen.getContext().getClassOrPackageParentContext(); 425 final Type owner; 426 if (ownerContext instanceof ClassContext) { 427 owner = typeMapper.mapClass(((ClassContext) ownerContext).getContextDescriptor()); 428 } 429 else if (ownerContext instanceof PackageContext) { 430 owner = ((PackageContext) ownerContext).getPackagePartType(); 431 } 432 else { 433 throw new UnsupportedOperationException("Unknown context: " + ownerContext); 434 } 435 436 codegen.tempVariables.put( 437 resolvedCall.getCall().getValueArguments().get(propertyMetadataArgumentIndex).asElement(), 438 new StackValue(PROPERTY_METADATA_TYPE) { 439 @Override 440 public void put(Type type, InstructionAdapter v) { 441 v.getstatic(owner.getInternalName(), JvmAbi.PROPERTY_METADATA_ARRAY_NAME, "[" + PROPERTY_METADATA_TYPE); 442 v.iconst(indexInPropertyMetadataArray); 443 StackValue.arrayElement(PROPERTY_METADATA_TYPE).put(type, v); 444 } 445 } 446 ); 447 448 StackValue delegatedProperty = codegen.intermediateValueForProperty(propertyDescriptor, true, null); 449 return codegen.invokeFunction(resolvedCall, delegatedProperty); 450 } 451 452 private static class DelegatedPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> { 453 private final int index; 454 455 public DelegatedPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor, int index) { 456 super(state, descriptor); 457 this.index = index; 458 } 459 460 @Override 461 public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { 462 InstructionAdapter v = codegen.v; 463 464 BindingContext bindingContext = state.getBindingContext(); 465 ResolvedCall<FunctionDescriptor> resolvedCall = 466 bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor); 467 assert resolvedCall != null : "Resolve call should be recorded for delegate call " + signature.toString(); 468 469 StackValue lastValue = invokeDelegatedPropertyConventionMethod(callableDescriptor.getCorrespondingProperty(), 470 codegen, state.getTypeMapper(), resolvedCall, index, 1); 471 Type asmType = signature.getReturnType(); 472 lastValue.put(asmType, v); 473 v.areturn(asmType); 474 } 475 } 476 477 @NotNull 478 public static String getterName(Name propertyName) { 479 return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); 480 } 481 482 @NotNull 483 public static String setterName(Name propertyName) { 484 return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); 485 } 486 487 public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor delegateTo, @NotNull StackValue field) { 488 ClassDescriptor toClass = (ClassDescriptor) delegateTo.getContainingDeclaration(); 489 490 PropertyGetterDescriptor getter = delegate.getGetter(); 491 if (getter != null) { 492 //noinspection ConstantConditions 493 functionCodegen.genDelegate(getter, delegateTo.getGetter().getOriginal(), toClass, field); 494 } 495 496 PropertySetterDescriptor setter = delegate.getSetter(); 497 if (setter != null) { 498 //noinspection ConstantConditions 499 functionCodegen.genDelegate(setter, delegateTo.getSetter().getOriginal(), toClass, field); 500 } 501 } 502 }