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