001 /* 002 * Copyright 2010-2013 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.text.StringUtil; 020 import org.jetbrains.annotations.NotNull; 021 import org.jetbrains.annotations.Nullable; 022 import org.jetbrains.asm4.FieldVisitor; 023 import org.jetbrains.asm4.MethodVisitor; 024 import org.jetbrains.asm4.Opcodes; 025 import org.jetbrains.asm4.Type; 026 import org.jetbrains.asm4.commons.InstructionAdapter; 027 import org.jetbrains.jet.codegen.context.CodegenContext; 028 import org.jetbrains.jet.codegen.context.FieldOwnerContext; 029 import org.jetbrains.jet.codegen.signature.JvmMethodSignature; 030 import org.jetbrains.jet.codegen.state.GenerationState; 031 import org.jetbrains.jet.codegen.state.GenerationStateAware; 032 import org.jetbrains.jet.codegen.state.JetTypeMapper; 033 import org.jetbrains.jet.lang.descriptors.*; 034 import org.jetbrains.jet.lang.psi.*; 035 import org.jetbrains.jet.lang.resolve.BindingContext; 036 import org.jetbrains.jet.lang.resolve.DescriptorResolver; 037 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 038 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 039 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; 040 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 041 import org.jetbrains.jet.lang.resolve.name.FqName; 042 import org.jetbrains.jet.lang.resolve.name.Name; 043 import org.jetbrains.jet.lang.types.ErrorUtils; 044 import org.jetbrains.jet.lang.types.JetType; 045 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 046 047 import static org.jetbrains.asm4.Opcodes.*; 048 import static org.jetbrains.jet.codegen.AsmUtil.getDeprecatedAccessFlag; 049 import static org.jetbrains.jet.codegen.AsmUtil.getVisibilityForSpecialPropertyBackingField; 050 import static org.jetbrains.jet.codegen.CodegenUtil.getParentBodyCodegen; 051 import static org.jetbrains.jet.codegen.CodegenUtil.isInterface; 052 import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; 053 054 public class PropertyCodegen extends GenerationStateAware { 055 @NotNull 056 private final FunctionCodegen functionCodegen; 057 058 @NotNull 059 private final ClassBuilder v; 060 061 @NotNull 062 private final FieldOwnerContext context; 063 064 @Nullable 065 private MemberCodegen classBodyCodegen; 066 067 @NotNull 068 private final OwnerKind kind; 069 070 public PropertyCodegen( 071 @NotNull FieldOwnerContext context, 072 @NotNull ClassBuilder v, 073 @NotNull FunctionCodegen functionCodegen, 074 @Nullable MemberCodegen classBodyCodegen 075 ) { 076 super(functionCodegen.getState()); 077 this.v = v; 078 this.functionCodegen = functionCodegen; 079 this.context = context; 080 this.classBodyCodegen = classBodyCodegen; 081 this.kind = context.getContextKind(); 082 } 083 084 public void gen(JetProperty p) { 085 PropertyDescriptor propertyDescriptor = DescriptorUtils.getPropertyDescriptor(p, bindingContext); 086 assert kind instanceof OwnerKind.StaticDelegateKind || kind == OwnerKind.NAMESPACE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.TRAIT_IMPL 087 : "Generating property with a wrong kind (" + kind + "): " + propertyDescriptor; 088 089 if (kind instanceof OwnerKind.StaticDelegateKind) { 090 FqName fqName = ((OwnerKind.StaticDelegateKind) kind).getOwnerClass().getFqName(); 091 v.getMemberMap().recordSrcClassNameForCallable(propertyDescriptor, fqName.shortName()); 092 } 093 else if (kind != OwnerKind.TRAIT_IMPL) { 094 generateBackingField(p, propertyDescriptor); 095 } 096 097 generateGetter(p, propertyDescriptor, p.getGetter()); 098 generateSetter(p, propertyDescriptor, p.getSetter()); 099 100 context.recordSyntheticAccessorIfNeeded(propertyDescriptor, typeMapper); 101 } 102 103 public void generatePrimaryConstructorProperty(JetParameter p, PropertyDescriptor descriptor) { 104 generateBackingField(p, descriptor); 105 generateGetter(p, descriptor, null); 106 if (descriptor.isVar()) { 107 generateSetter(p, descriptor, null); 108 } 109 } 110 111 public void generateConstructorPropertyAsMethodForAnnotationClass(JetParameter p, PropertyDescriptor descriptor) { 112 Type type = state.getTypeMapper().mapType(descriptor); 113 String name = p.getName(); 114 assert name != null : "Annotation parameter has no name: " + p.getText(); 115 MethodVisitor visitor = v.newMethod(p, ACC_PUBLIC | ACC_ABSTRACT, name, "()" + type.getDescriptor(), null, null); 116 JetExpression defaultValue = p.getDefaultValue(); 117 if (defaultValue != null) { 118 CompileTimeConstant<?> constant = state.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, defaultValue); 119 assert constant != null : "Default value for annotation parameter should be compile time value: " + defaultValue.getText(); 120 AnnotationCodegen annotationCodegen = AnnotationCodegen.forAnnotationDefaultValue(visitor, typeMapper); 121 annotationCodegen.generateAnnotationDefaultValue(constant); 122 } 123 } 124 125 private void generateBackingField(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) { 126 boolean hasBackingField = Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)); 127 boolean isDelegated = p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null; 128 if (hasBackingField || isDelegated) { 129 DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration(); 130 if (isInterface(containingDeclaration)) { 131 return; 132 } 133 134 FieldVisitor fieldVisitor = hasBackingField 135 ? generateBackingFieldAccess(p, propertyDescriptor) 136 : generatePropertyDelegateAccess((JetProperty) p, propertyDescriptor); 137 138 AnnotationCodegen.forField(fieldVisitor, typeMapper).genAnnotations(propertyDescriptor); 139 } 140 else if (!propertyDescriptor.getAnnotations().isEmpty()) { 141 // Annotations on properties without backing fields are stored in bytecode on an empty synthetic method. This way they're still 142 // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally 143 String methodName = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(propertyDescriptor.getName()); 144 MethodVisitor mv = v.newMethod(null, 145 ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, 146 methodName, 147 JvmAbi.ANNOTATED_PROPERTY_METHOD_SIGNATURE, 148 null, 149 null); 150 v.getMemberMap().recordSyntheticMethodNameOfProperty(propertyDescriptor, methodName); 151 AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(propertyDescriptor); 152 mv.visitCode(); 153 mv.visitInsn(Opcodes.RETURN); 154 mv.visitEnd(); 155 } 156 } 157 158 private FieldVisitor generateBackingField(JetNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, JetType jetType, Object defaultValue) { 159 int modifiers = getDeprecatedAccessFlag(propertyDescriptor); 160 161 if (KotlinBuiltIns.getInstance().isVolatile(propertyDescriptor)) { 162 modifiers |= ACC_VOLATILE; 163 } 164 165 if (kind == OwnerKind.NAMESPACE) { 166 modifiers |= ACC_STATIC; 167 } 168 169 if (!propertyDescriptor.isVar() || isDelegate) { 170 modifiers |= ACC_FINAL; 171 } 172 173 Type type = typeMapper.mapType(jetType); 174 175 ClassBuilder builder = v; 176 177 FieldOwnerContext backingFieldContext = context; 178 if (AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor)) { 179 modifiers |= ACC_STATIC | getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegate); 180 ImplementationBodyCodegen codegen = getParentBodyCodegen(classBodyCodegen); 181 builder = codegen.v; 182 backingFieldContext = codegen.context; 183 v.getMemberMap().recordStaticFieldInOuterClass(propertyDescriptor); 184 } else { 185 if (kind != OwnerKind.NAMESPACE || isDelegate) { 186 modifiers |= ACC_PRIVATE; 187 } 188 } 189 190 if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) { 191 ImplementationBodyCodegen parentBodyCodegen = getParentBodyCodegen(classBodyCodegen); 192 parentBodyCodegen.addClassObjectPropertyToCopy(propertyDescriptor, defaultValue); 193 } 194 195 String name = backingFieldContext.getFieldName(propertyDescriptor, isDelegate); 196 197 v.getMemberMap().recordFieldOfProperty(propertyDescriptor, type, name); 198 199 return builder.newField(element, modifiers, name, type.getDescriptor(), 200 typeMapper.mapFieldSignature(jetType), defaultValue); 201 } 202 203 private FieldVisitor generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) { 204 JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression()); 205 if (delegateType == null) { 206 // If delegate expression is unresolved reference 207 delegateType = ErrorUtils.createErrorType("Delegate type"); 208 } 209 210 return generateBackingField(p, propertyDescriptor, true, delegateType, null); 211 } 212 213 private FieldVisitor generateBackingFieldAccess(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor) { 214 Object value = null; 215 216 if (ImplementationBodyCodegen.shouldWriteFieldInitializer(propertyDescriptor, typeMapper)) { 217 JetExpression initializer = p instanceof JetProperty ? ((JetProperty) p).getInitializer() : null; 218 if (initializer != null) { 219 CompileTimeConstant<?> compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer); 220 value = compileTimeValue != null ? compileTimeValue.getValue() : null; 221 } 222 } 223 224 return generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value); 225 } 226 227 private void generateGetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor getter) { 228 boolean defaultGetter = getter == null || getter.getBodyExpression() == null; 229 230 //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter 231 //if (!defaultGetter || isExternallyAccessible(propertyDescriptor)) { 232 JvmMethodSignature signature = typeMapper.mapGetterSignature(propertyDescriptor, kind); 233 PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter(); 234 getterDescriptor = getterDescriptor != null ? getterDescriptor : DescriptorResolver.createDefaultGetter(propertyDescriptor); 235 236 if (kind != OwnerKind.TRAIT_IMPL || !defaultGetter) { 237 FunctionGenerationStrategy strategy; 238 if (defaultGetter) { 239 if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) { 240 strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, getterDescriptor); 241 } 242 else { 243 strategy = new DefaultPropertyAccessorStrategy(state, getterDescriptor); 244 } 245 } 246 else { 247 strategy = new FunctionGenerationStrategy.FunctionDefault(state, getterDescriptor, getter); 248 } 249 functionCodegen.generateMethod(getter != null ? getter : p, 250 signature, 251 getterDescriptor, 252 strategy); 253 } 254 //} 255 } 256 257 private void generateSetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor setter) { 258 boolean defaultSetter = setter == null || setter.getBodyExpression() == null; 259 260 //TODO: Now it's not enough information to properly resolve property from bytecode without generated getter and setter 261 if (/*!defaultSetter || isExternallyAccessible(propertyDescriptor) &&*/ propertyDescriptor.isVar()) { 262 JvmMethodSignature signature = typeMapper.mapSetterSignature(propertyDescriptor, kind); 263 PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter(); 264 setterDescriptor = setterDescriptor != null ? setterDescriptor : DescriptorResolver.createDefaultSetter(propertyDescriptor); 265 266 if (kind != OwnerKind.TRAIT_IMPL || !defaultSetter) { 267 FunctionGenerationStrategy strategy; 268 if (defaultSetter) { 269 if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) { 270 strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, setterDescriptor); 271 } 272 else { 273 strategy = new DefaultPropertyAccessorStrategy(state, setterDescriptor); 274 } 275 } 276 else { 277 strategy = new FunctionGenerationStrategy.FunctionDefault(state, setterDescriptor, setter); 278 } 279 functionCodegen.generateMethod(setter != null ? setter : p, 280 signature, 281 setterDescriptor, 282 strategy); 283 } 284 } 285 } 286 287 288 private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> { 289 290 public DefaultPropertyAccessorStrategy( 291 @NotNull GenerationState state, 292 @NotNull PropertyAccessorDescriptor callableDescriptor 293 ) { 294 super(state, callableDescriptor); 295 } 296 297 @Override 298 public void doGenerateBody( 299 ExpressionCodegen codegen, JvmMethodSignature signature 300 ) { 301 generateDefaultAccessor(callableDescriptor, codegen.v, codegen); 302 } 303 } 304 305 private static void generateDefaultAccessor( 306 @NotNull PropertyAccessorDescriptor accessorDescriptor, 307 @NotNull InstructionAdapter iv, 308 @NotNull ExpressionCodegen codegen 309 ) { 310 JetTypeMapper typeMapper = codegen.typeMapper; 311 CodegenContext context = codegen.context; 312 OwnerKind kind = context.getContextKind(); 313 314 PropertyDescriptor propertyDescriptor = accessorDescriptor.getCorrespondingProperty(); 315 Type type = typeMapper.mapType(propertyDescriptor); 316 317 int paramCode = 0; 318 if (kind != OwnerKind.NAMESPACE) { 319 iv.load(0, OBJECT_TYPE); 320 paramCode = 1; 321 } 322 323 StackValue property = codegen.intermediateValueForProperty(accessorDescriptor.getCorrespondingProperty(), true, null); 324 325 if (accessorDescriptor instanceof PropertyGetterDescriptor) { 326 property.put(type, iv); 327 iv.areturn(type); 328 } 329 else if (accessorDescriptor instanceof PropertySetterDescriptor) { 330 ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter(); 331 if (receiverParameter != null) { 332 paramCode += typeMapper.mapType(receiverParameter.getType()).getSize(); 333 } 334 iv.load(paramCode, type); 335 336 property.store(type, iv); 337 iv.visitInsn(RETURN); 338 } else { 339 assert false : "Unreachable state"; 340 } 341 } 342 343 private static class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> { 344 public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) { 345 super(state, descriptor); 346 } 347 348 @Override 349 public void doGenerateBody( 350 @NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature 351 ) { 352 JetTypeMapper typeMapper = codegen.typeMapper; 353 OwnerKind kind = codegen.context.getContextKind(); 354 InstructionAdapter iv = codegen.v; 355 BindingContext bindingContext = state.getBindingContext(); 356 357 ResolvedCall<FunctionDescriptor> resolvedCall = 358 bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, callableDescriptor); 359 360 Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, callableDescriptor); 361 assert call != null : "Call should be recorded for delegate call " + signature.toString(); 362 363 PropertyDescriptor property = callableDescriptor.getCorrespondingProperty(); 364 Type asmType = typeMapper.mapType(property); 365 366 if (kind != OwnerKind.NAMESPACE) { 367 iv.load(0, OBJECT_TYPE); 368 } 369 370 StackValue delegatedProperty = codegen.intermediateValueForProperty(property, true, null); 371 StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall); 372 373 if (lastValue.type != Type.VOID_TYPE) { 374 lastValue.put(asmType, iv); 375 iv.areturn(asmType); 376 } 377 else { 378 iv.areturn(Type.VOID_TYPE); 379 } 380 } 381 } 382 383 public static String getterName(Name propertyName) { 384 return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); 385 } 386 387 public static String setterName(Name propertyName) { 388 return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString()); 389 } 390 391 public void genDelegate(PropertyDescriptor delegate, PropertyDescriptor overridden, StackValue field) { 392 ClassDescriptor toClass = (ClassDescriptor) overridden.getContainingDeclaration(); 393 394 functionCodegen.genDelegate(delegate.getGetter(), toClass, field, 395 typeMapper.mapGetterSignature(delegate, OwnerKind.IMPLEMENTATION), 396 typeMapper.mapGetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION)); 397 398 if (delegate.isVar()) { 399 functionCodegen.genDelegate(delegate.getSetter(), toClass, field, 400 typeMapper.mapSetterSignature(delegate, OwnerKind.IMPLEMENTATION), 401 typeMapper.mapSetterSignature(overridden.getOriginal(), OwnerKind.IMPLEMENTATION)); 402 } 403 } 404 }