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